Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 在D中实现值对象模式_Design Patterns_Constants_D_Immutability_Value Objects - Fatal编程技术网

Design patterns 在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);

我想在D中实现值对象模式。也就是说,我想让可变的引用变量指向不可变的对象
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 */
}