Design patterns 在D中实现值对象模式
我想在D中实现值对象模式。也就是说,我想让可变的引用变量指向不可变的对象Design patterns 在D中实现值对象模式,design-patterns,constants,d,immutability,value-objects,Design Patterns,Constants,D,Immutability,Value Objects,我想在D中实现值对象模式。也就是说,我想让可变的引用变量指向不可变的对象T变量应该是可赋值的,但是T对象永远不应该改变它们的状态 我对D中的const和immutable之间的区别感到困惑。让我用一个骨架Rational类来说明我的疑问: class Rational { int num; int den; 我应该将num和den声明为const还是不可变?整数有区别吗 invariant() { assert(den > 0);
T
变量应该是可赋值的,但是T
对象永远不应该改变它们的状态
我对D中的const
和immutable
之间的区别感到困惑。让我用一个骨架Rational
类来说明我的疑问:
class Rational
{
int num;
int den;
我应该将num
和den
声明为const
还是不可变
?整数有区别吗
invariant()
{
assert(den > 0);
assert(gcd(abs(num), den) == 1);
}
我应该将不变量
声明为常量
还是不可变
?将其标记为不可变
会导致编译时错误,但这可能是由于其他成员未被标记为不可变
this(int numerator, int denominator) { ... }
我应该将构造函数声明为常量
还是不可变
?那是什么意思
string toString()
{
return std.string.format("(%s / %s)", num, den);
}
}
我应该将toString
声明为const
还是不可变
除了标记单个成员,我似乎还可以标记整个班级:
class Rational
const class Rational
immutable class Rational
以下哪项对于值对象模式最有意义
那纯呢?在值对象模式中,方法应该没有副作用,因此将每个成员声明为纯是否有意义?不幸的是,将toString
标记为pure
不会编译,因为std.string.format
不是pure;有什么特别的原因吗
似乎我也可以将类本身声明为纯
,但这似乎没有任何效果,因为编译器不再抱怨toString
调用不纯函数
那么,将一个类声明为pure
意味着什么呢?它只是被忽略了吗?D结构
值对象模式最好通过简单地使用a及其内置的值语义在D中表示
据我所知,由于Java目前缺乏具有值语义的内置聚合,Java中通常采用值对象模式
D结构与C和C中的结构类似,C++中的结构和类也有类似的作用。这种比较可能最适合后者,因为D结构有构造函数和析构函数,但有一个重要的例外:没有继承和虚函数;这些特性被委托给了Java和C#中的类(它们是隐式引用类型,因此从不显示)
Rational的实例总是按值传递给函数(除非参数明确指定了其他内容,请参阅),并在赋值时复制
纯洁
纯函数不能读取或写入任何全局状态。纯函数允许对显式参数以及隐式方法的此参数进行变异;因此,Rational上的方法可能总是纯
std.string.format
不是pure
是其当前实现的一个问题。它将在将来使用另一种实现,即纯
常量和不变
如果要表示该方法是纯的,并且不改变其自身的状态,则可以将其设置为pure
和const
可变(Rational
)和不可变(immutable(Rational)
)实例都可以隐式转换为const(Rational)
,因此当您不需要不可变保证但仍然不变异任何成员时,const
是最佳选择
通常,不需要改变成员字段的结构方法应该是const
。对于类,同样适用,但您还必须考虑任何可能重写该方法的派生方法-它们受相同限制的约束
将const
或immutable
放在struct
或class
声明上相当于分别标记其所有成员(包括方法)const
或immutable
不可变构造函数
如果构造函数所做的只是将num
和den
字段分配给各自的构造函数参数,则默认情况下,此功能已存在于结构上:
struct S { int foo, bar; }
auto s = S(1, 2);
assert(s.foo == 1);
assert(s.bar == 2);
构造函数上的const
没有多大意义,因为任何构造函数无论是否恒定都可以构造一个const实例,因为所有内容都可以隐式转换为const
构造函数上的immutable
是有意义的,有时也是构造结构或类的不可变实例的唯一方法。可变构造函数可以为此
引用创建别名,通过该别名实例可以在以后进行变异,因此其结果不能始终隐式转换为不可变
然而,在您的案例中不需要不可变构造函数,因为Rational没有任何间接寻址,所以可以使用可变构造函数并复制结果。换句话说,没有可变间接寻址的类型可以隐式转换为不可变。这包括基本类型,如int
和float
以及满足相同条件的结构
没有效果的属性
所有当前编译器都会忽略放在声明上的属性,这些属性对声明没有任何影响。这是有意义的,因为属性可以一次应用于多个声明,使用属性{/*declarations*/}
和属性:/*declarations*/
语法:
struct S
{
immutable
{
int foo;
int bar;
}
}
struct S2
{
immutable:
int foo;
int bar;
}
在上述两个示例中,foo
和bar
属于不可变(int)
类型
使用类
有时不需要值语义,例如与频繁复制大型结构相关的性能原因。可以通过引用显式传递结构,例如使用ref
struct S
{
immutable
{
int foo;
int bar;
}
}
struct S2
{
immutable:
int foo;
int bar;
}
class Rational
{
immutable int num;
immutable int den;
this(int num, int den)
{
this.num = num;
this.den = den;
}
/* methods here */
}
class Rational
{
int num;
int den;
this(int num, int den)
{
this.num = num;
this.den = den;
}
/* immutable constructor overload would be here */
/* methods here */
}