原文
我有一个许多类
实现的接口,我想随机
选择其中一个
实现.还有两个约束
:首先,不是均匀分布
,所有类都可定义被选中
的机会(反映在"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];
else
return [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();
}
}