组合模型
上篇我们使用命令模式,帮助生产员小啃💂更好的生活。
啃得鸡的生产线通过引入自动生产线,产量激增;但销售线并没办法通过科技爆炸实现销售的激增。
所以啃得鸡决定,把部分商品通过让利的方式给到经销商销售;走薄利多销的方式消化产能。
大经销商,也通过让利的方式将商品给到小经销商;最终给到消费者手中。
代码上怎么实现?
# 场景分析
这里可以看到
啃得鸡厂家,对应有N个大经销商 大经销商,对应有N个小经销商 ... 小经销商,对应N个零售商 零售商,对应N个顾客
可以发现在整个销售链是一个树状结构:
厂家 -> n大经销商 -> ... -> n零售商 -> n顾客
厂家扮演了【提供商】,顾客扮演了【消费者】; 其他都同时扮演了两个角色:【消费者】、【提供商】;
在销售的整个链条中,每一层都只关注自己上下游,对其他的并不多过问。
如果将【消费者】、【提供商】抽提为接口就是一个行为 buy; 同时实现类做父子结构嵌套,即完成整个链条的表述。
# 实现
# 接口Component
class Component
{
public:
explicit Component(string key) : m_sKey(key) , m_pParent(nullptr) {}
virtual ~Component() {}
public:
virtual void add(string key, Component* component) {}
virtual void remove(string key) {}
virtual Component* getParent(){ return m_pParent; }
virtual void setParent(Component* component) { m_pParent = component; }
virtual Component* getChild() { return nullptr; }
virtual void refresh() {}
public:
virtual int buy(int num) { return 0; }
virtual int needNum() { return 0; }
protected:
string m_sKey;
Component* m_pParent;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 叶子 Leaf
class Leaf : public Component
{
public:
explicit Leaf(string key) : Component(key) {}
public:
int buy(int num)
{
cout << m_sKey << "购买了" << num << "根雪糕!" << endl;
return 0;
}
int needNum()
{
return 1;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 一般节点:Composite
class Composite : public Component
{
public:
Composite(string key)
: Component(key)
, m_nNeedNum(0)
{
}
virtual ~Composite()
{
m_oChildren.clear();
}
public:
void add(Component* component)
{
m_oChildren.push_back(component);
component->setParent(this);
refresh();
}
void remove(int index)
{
m_oChildren.erase(m_oChildren.begin() + index);
refresh();
}
Component* getChild(int index)
{
return m_oChildren[index];
}
void refresh()
{
m_nNeedNum = 0;
for each (auto var in m_oChildren)
{
m_nNeedNum += var->needNum();
}
if (nullptr != getParent())
{
getParent()->refresh();
}
}
public:
int buy(int num)
{
cout << m_sKey << "进货" << num << "根雪糕!" << endl;
for each (auto var in m_oChildren)
{
num -= var->needNum();
var->buy(var->needNum()) ;
}
return num;
}
int needNum()
{
return m_nNeedNum;
}
private:
int m_nNeedNum;
vector<Component*> m_oChildren;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# main函数
int main(int argc, char* argv[])
{
Composite oComposite("厂家");
Composite oComposite1("代理商");
Composite oComposite2("零售商1");
Composite oComposite3("零售商2");
Leaf oLeaf1("顾客小王");
Leaf oLeaf2("顾客小李");
oComposite.add(&oComposite1);
oComposite1.add(&oComposite2);
oComposite1.add(&oComposite3);
oComposite2.add(&oLeaf1);
oComposite3.add(&oLeaf2);
oComposite.buy(2);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行结果:
# 代码位置
仓库位置:https://github.com/su-dd/demo.git (opens new window)
代码位置:设计模式/Component (opens new window)
# 感悟
组合模式又叫 “部分整体模式”,这个名字可以更好的表达这个模式想要解决的问题。
及:事物的 部分和整体具有高度相似性。
在组合模式中,每个节点的类定义中,都可以继续包含一组和自己相同的对象;
叶子节点的定义不是必须,但叶子节点一般是必然存在的(数据是有限的)。
# 关于接口定义
本案例中接口的定义除了唯一的业务接口为 buy外,还包含对节点的操作:add,remove,getChild, getParent等非业务功能定义;
这意味着,可以客户端可以统一的操作叶子节点和根节点,这种方式也叫透明组合模式;
这种方式也意味着一个问题,客户端在叶子节点调用 add ,remove等操作导致失败;但客户端不知道,具有一定的安全性问题。
于是为了解决这个问题,又发明出一个安全组合模式
这里接口将不存在add ,remove等操作,客户端需要明确知道当前节点的类型才能做对应操作。