原文
我有一个许多类
实现的接口,我想随机
选择其中一个
实现.还有两个约束
:首先,不是均匀分布
,所有类都可定义被选中
的机会(反映在"weight()"
函数中).而且根据运行时信息
,并非所有类
都可用.
我搞了个最小
工作示例,但它很重
(特别是因为有中间枚举
),可更漂亮,更易于理解(也许用模板插件
等编译时操作).
可否改进?
import std.stdio;
import std.array;
import std.algorithm;
import std.conv;
import std.random;
interface Parent
{void doWork();static int weight();static Parent from(Implem impl){final switch (impl){case Implem.IMPLEM_1:return new Implem1();case Implem.IMPLEM_2:return new Implem2();case Implem.IMPLEM_3:return new Implem3();}}
}
class Implem1 : Parent
{void doWork(){writeln("From Implem 1");}static int weight(){return 3;}
}
class Implem2 : Parent
{void doWork(){writeln("From Implem 2");}static int weight(){return 2;}
}
class Implem3 : Parent
{void doWork(){writeln("From Implem 3");}static int weight(){return 3;}
}
enum Implem
{IMPLEM_1,IMPLEM_2,IMPLEM_3
};Implem[] availableImplems()
{bool runtimeCondition = true;if (runtimeCondition)return [Implem.IMPLEM_1, Implem.IMPLEM_2];elsereturn [Implem.IMPLEM_2, Implem.IMPLEM_3];
}
int getWeight(Implem implem)
{final switch (implem){case Implem.IMPLEM_1:return Implem1.weight();case Implem.IMPLEM_2:return Implem2.weight();case Implem.IMPLEM_3:return Implem3.weight();}
}
int[] getAllWeights(in Implem[] availableImplems)
{return availableImplems.map!(implem => implem.getWeight()).array;
}
Parent drawAtRandom()
{const Implem[] available = availableImplems();const int[] weights = getAllWeights(available);const Implem drawn = available[dice(weights)];return Parent.from(drawn);
}
void main()
{Parent p = drawAtRandom();p.doWork();
}
我会用编译时
枚举标记
每个实现,并用带CRTP[1]
的编译时内省
,并根据期望分布
选择类来自动
生成代码.
1维基CRTP
如下:
import std.stdio;
interface MyIntf {void work();
}struct ImplemInfo {int weight;MyIntf function() instantiate;
}ImplemInfo[] implems; //实现列表
int totalWeight;MyIntf chooseImplem() {import std.random;auto pick = uniform(0, totalWeight);auto slice = implems[];assert(slice.length > 0);while (slice[0].weight <= pick) {pick -= slice[0].weight;slice = slice[1 .. $];}return slice[0].instantiate();
}//使用`CRTP`在`.implems`中`自动注册`实现的`基类`,从而无需`每个子类`中的太多样板.
class Base(C) : MyIntf {//继承类必须定义一个`编译时`可读的`.weight`成员.static assert(is(typeof(C.weight) : int),"继承类必须定义.weight成员");static this() {implems ~= ImplemInfo(C.weight, () {return cast(MyIntf) new C;});totalWeight += C.weight;}//继承类必须实现它abstract void work();//..
}
//类位置
class Implem1 : Base!Implem1 {enum weight = 1;override void work() { writeln(typeof(this).stringof); }
}
class Implem2 : Base!Implem2 {enum weight = 2;override void work() { writeln(typeof(this).stringof); }
}
class Implem3 : Base!Implem3 {enum weight = 3;override void work() { writeln(typeof(this).stringof); }
}
void main() {//通过`管道`把程序输出传递给`"sort|uniq-c"`来验证是否`正确`生成了`期望分布`.foreach (_; 0 .. 100) {auto impl = chooseImplem();impl.work();}
}