领域驱动设计

把​​值对象​​​视为​​不变​​​的.使​​无副作用​​​函数,不依赖​​可变状态​​​.
域事件一般​​​不变​​​,不应​​修改​​​域代码​​操作​​​的数据,相反,总是返回​​新数据​​​.
大量使用​​​UFCS​​​域链来限制​​使用内存​​​.
最好静态保证!

想用​​不变​​,传统上:

struct ArrayContainer
{
@ConstRead
private int[] array_;
...
mixin(GenerateFieldAccessors);
}

但,基于模板:​​构建慢,内存使用率高​​​.​​脆弱​​​,对编译器更改​​敏感​​​,不可传递:需要​​array_​​​的潜在​​副本​

想要:

immutable struct ArrayContainer
{
int[] array;
}

但不能用​​不变​​​!
● 想使用​​​不变​​​数据类型.
● 想使用​​​区间​​​.
● 还使用了很多​​​关联数组​​​.
● 但是​​​不变​​​数据类型打破了许多​​区间​​​,并且不适合​​关联数组​​!

immutable struct S { int i; }
...
auto list = [S(2), S(4), S(3)];

错误,不能修改​​...​​​.不能用​​不变​​.

immutable struct S { int i; }
...
auto list = [S(2), S(4), S(3)];
assert(list.maxElement!"a.i"== S(4);
//错误!无法修改.

为何?如下,很明显应工作:

T fun(T)(T arg1, T arg2) {
T result = arg1;
result = arg2;
return result;
}

不能用​​Unqual​​!

T fun(T)(T foo, T bar) { Unqual!T result = foo; result = bar; return result; }

​Unqual​​​,只是去掉​​最外层​​​的​​限定符​​​.字段,仍然是​​不变​​的.

immutable struct S { int i; }
void main() {
Unqual!S s;
static assert(!is(typeof(s) == S));
s = S(5);
}
//错误!

如果覆盖不变,会怎样?

immutable struct S {
int field;
}

void genericFun(T)(T first, T second) {
auto store = first;
immutable int* ptr = &store.field;
int firstField = *ptr;
store = second; // 危险!
int secondField = *ptr;
// 失败!
assert(firstField == secondField);
// 观察到了不变指针改变它的值,全部丢失,等等
}

问题,是不能修改​​不变​​​字段.​​观察​​​不变​​引用​​​,改变了​​不变​​​的​​值​​​.用​​头可变​​​来​​放松​​常系统?

struct Turducken(T) {
Turkey store;
struct Turkey {
Duck duck;
}
union Duck {
Chicken chicken;
}
alias Chicken = T;//`T`封装在`联`中的`构`中
//即使T有不变成员.
}

因为是​​联​​​,所以不调用​​析构器​​​.
因为是​​​结构体​​​,所以,即使T有​​不变​​​商店,也可用​​std.algorithm.mutation.moveEmplace()​​​.
可以控制生命期.故意的.
缺点是,未来可能会​​​改变​​.

如何写头可变

align(T.alignof)
struct HeadMut(T) {
void[T.sizeof] data;
}

​精确扫描​​​的​​GC​​​问题:始终按指针对待​​void[n]​​​.
想要​​​DeepUnqual​​​.用​​DeepUnqual​​​来帮助.
​​​rebindable.DeepUnqual​​​,为每个​​D数据类型​​​定义等效类型.​​可变​​​指针在一堆.​​不变​​​指针在​​另一堆​​.其他,都不一样.

​DeepUnqual!T​​​精确​​GC​​​,扫描​​扫描T的字段​​​.相同大小,相同对齐(​​中性​​​),同它工作,是深度​​不安全​​​!你要​​转换​​​一切.
​​​置(T)==>重绑定(T)(神秘)==>T 取()​​.

如何​​保存​​​T,如何取​​T​​?

Rebindable value;
value.set(S(5));
assert(value.get == S(5));
//即使S为不变.

但,​​安全​​吗?

是​​秘密​​​吗?​​安全​​​吗?
● 只要T是​​​秘密​​​的,它就是​​安全​​​的.
● ​​​D不变​​​混合了内存​​不变性​​​和​​可观察性​​​.
● 只要​​​未观察​​​到变化.就​​不在乎​​​值是否变化.
● ​​​Rebindable!T​​​是包装​​T​​​.
● 不能按​​​T形式​​​取包含​​数据​​​的​​引用​​​,因为​​get​​​按值返回.
● 由于​​​DeepUnqual​​​,存储的​​数据​​​是​​可变​​​的,因此可以改变​​Rebindable!T​​​.从不改变​​未声明​​​为​​可变​​​的​​内存​​​.
● 并且​​​内存​​​自身未按​​不变​​​公开,而是返回​​不变​​​的​​值副本​​.

虽然​​数据​​​存储在​​Rebindable​​​中,但它是可​​变化​​​的,但每次​​变化​​​都必须是从​​有效​​​状态到​​有效​​​状态,无法在​​变化过程​​​中​​抓到它​​​.
● 弱点:​​​T​​​不能依赖由​​构造函数​​​或者​​复制构造​​​函数创建的​​每个地址​​​.
● 如果​​​复制构造函数​​​到处搞乱​​字段地址​​​,就会​​中断​​.

还有:​​Nullable, rebindable.Nullable​​工具.

Nullable!(const int) ni;
assert(ni.isNull);
ni = 5;
assert(!ni.isNull && ni.get == 5);
ni.nullify;
assert(ni.isNull);

​不变安全​​​的关联数组,​​rebindable.AssocArray​

AssocArray!(int, S) assocArray;
assocArray[0] = S([5]);
assocArray[0] = S([6]);
assert(assocArray[0] == S([6]));

建议:​​可引用性​​​是​​万恶之源​​​.
● ​​​immutable​​​是不是​​太强大​​​了?应该可覆盖​​不变​​​字段吗?
● 否:​​​不变​​​太弱了!
● 问题是​​​可观察​​​到​​不变字段的变化​​​.
● 默认,可​​​&i​​​并取得​​i​​​的永久视图:​​&i​​​是​​immutable(int)*​​​,但可能改变值!
● 什么比​​​不变​​​更强大?​​右值​​!

​右值​​​:只能出现在​​右边​​​的,就叫​​右值​​.

假设右值结构​​{}​​​可比​​不变​​​结构更强大,但仍可通过分配​​新值​​​来覆盖!
​​​右值构​​​是纯数据结构:不取​​字段地址​​​,​​不引用​​​字段,​​不直接分配​​字段.

为什么?这样,就不必害怕​​突变​​​,因为没有人能​​抓住​​​我们.
● 如果只赋值​​​新构建​​​的​​T值​​​,则不会破坏​​不变性​​​.
● ​​​纯右值​​​比​​不变​​​更强:它是​​不可引用​​​的,因此是​​不可观察​​​的.
● 事实上,它没有​​​内存​​​:它仅是​​数据​​​,不保存在​​内存​​​中.
● 可读取​​​它的值​​​,但不能​​远程观察​​​它的字段,因为它是​​旧数据​​.

实际​​可行​​​建议:​​彻底删除​​​不变​​结构字段​​​.
为什么​​​Unqual!T​​​不起作用?为什么​​Unqual!T​​​不应工作?
​​​标准库​​​假定​​Unqual!T​​​造​​头可变​​​.
​​​结构内部​​​可隐藏​​不变​​​字段,创建​​独立​​​的​​不变补丁​​.

immutable struct S
{
//实际上,仅能通过`不变本`访问immutable(int)[]
int[] a;
}

​Unqual​​​已为​​构外​​​类型创建​​头可变​​​值.
​​​字段​​​实际上总是​​头可变​​​的,​​immutable(T)​​​仅为​​T var​​设置默认值.

实际上,​​不变​​​使每个字段为​​Unqual!(immutable T)​​​.
​​​直接​​​访问可变​​(T.field)​​​字段,先隐式转为​​不变​​​,如果不能,则是错误.
这保留了​​​常​​​,保留了​​不变​​​,​​不需要​​访问器.

但允许在数据结构中,通过​​Unqual!T​​​使用任何​​T​​​.效果:​​Unqual!T==HeadMutable!T​​​,总是.但未完全解决类的​​头可变​​​问题.(​​immutable(Object)​​​不会​​隐式​​​转换到​​Object​​​.).但是在​​域代码​​​中,不会使用类.​​:-)​​​无论如何,存在​​std.typecons.Rebindable​​.

​代码位置​