大牧絮叨设计模式:合成模式/组合模式

1、 合成模式概述1.1、 核心组件1.2、 优点缺陷2、 Java实现1)组件接口规范2)组件实现3)实现测试4)测试结果3、 Python实现4、 Go实现1) 合成/组合模式:抽象接口2) Leaf组件实现3) Composite实现4) 关于GO

1、 合成模式概述

合成模式[Composite]也称为部分-整体模式[Part-Whole]、组合模式,合成模式中将对象组织到树形结构中,针对同一种对象描述整体和部分的关系,合成模式下可以将简单对象和组合对象同等对待。

提到合成模式,总会想起这样一个故事 从前有座山,山里有座庙,庙里有个老和尚,庙里有个小和尚,老和尚给小和尚讲故事,讲的什么故事呢?从前有座山…

随笔涂鸦,不要见笑

在项目开发的某些情况下需要类似树形结构的对象架构,如项目中常规的组织结构关系,权限管理关系、文件目录结构等等,都需要通过类似树形结构的软件架构进行定义,合成模式就是针对这样的结构的一种通用解决方案。

1.1、 核心组件

合成模式中核心主要包含两部分组件,包含下级节点的组件和不包含下级节点的组件,如果形容成一个树形结构的话我们称为 树枝节点树叶节点

image.png

合成模式中,根据树枝节点和树叶节点两种节点的是否一致性,区分为透明形式安全形式,不论哪种处理形式都是通过组件的嵌套组合完成部分-整体的关联关系,通过这样的关联关系表达树形结构。

1.2、 优点缺陷

优点:

高层模块调用简单, 节点自由增加。

缺陷:

在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则,同时不容易限制组合中的组件。

2、 Java实现

1)组件接口规范

package com.damu;

import com.sun.org.apache.xpath.internal.operations.String;

import java.util.List;

/**
 * <p>项目文档: 合成模式 接口规范 </p>
 *
 * @author <a href="https://github.com/laomu/laomu.github.io">大牧</a>
 * @version V1.0
 */
public interface IComponent {
    /**
     * 增加节点
     * @param component 要增加的节点对象
     */
    IComponent add(IComponent component);

    /**
     * 删除节点
     * @param component 要删除的节点对象
     */
    IComponent remove(IComponent component);

    /**
     * 判断是否包含子节点
     * @return 返回判断结果
     */
    Boolean hasChildComponent();

    /**
     * 获取自身节点
     * @return 返回当前节点
     */
    IComponent getComponentSelf();

    /**
     * 获取所有子节点
     * @return 所有子节点
     */
    List<IComponent> getComponentsChild();

    /**
     * 遍历节点信息
     */
    static void lookUpComponent(IComponent component, Integer depth) {
        StringBuffer sb = new StringBuffer("");
        for (int i = 0; i < depth; i++) {
            sb.append("--");
        }

        if (component.hasChildComponent()) {
            System.out.println(sb + "Composite: " + ((Composite)component).getData());
            // 获取所有子节点
            for (IComponent childComponent : component.getComponentsChild()) {
                IComponent.lookUpComponent(childComponent, depth + 2);
            }
            return;
        }
        System.out.println(sb + "Leaf: " + ((Composite)component).getData());
    }
}

2)组件实现

package com.damu;

import java.util.List;
import java.util.Vector;

/**
 * <p>项目文档: 树枝节点 </p>
 *
 * @author <a href="https://github.com/laomu/laomu.github.io">大牧</a>
 * @version V1.0
 */
public class Composite implements IComponent {

    // 子节点集合
    private Vector<IComponent> vector = new Vector<IComponent>();
    // 当前节点数据
    private String data;

    public Composite(String data) {
        this.data = data;
    }

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    @Override
    public IComponent add(IComponent component) {
        this.vector.add(component);
        return this;
    }

    @Override
    public IComponent remove(IComponent component) {
        this.vector.remove(component);
        return this;
    }

    @Override
    public Boolean hasChildComponent() {
        return this.vector.size() > 0;
    }

    @Override
    public IComponent getComponentSelf() {
        return this;
    }

    @Override
    public List<IComponent> getComponentsChild() {
        return this.vector;
    }
}

3)实现测试

package com.damu;

/**
 * <p>项目文档: 合成模式 透明状态 测试代码</p>
 *
 * @author <a href="https://github.com/laomu/laomu.github.io">大牧</a>
 * @version V1.0
 */
public class CompositeMain {

    public static void main(String[] args) {
        Composite story = new Composite("开始讲故事...");

        Composite mount = new Composite("从前有座山");
        Composite temple = new Composite("山里有座庙");
        Composite buddhistMonk = new Composite("庙里有个老和尚");
        Composite childBuddhistMonk = new Composite("庙里有个小和尚");

        // 从前有座山...loop 1 -> n
        IComponent thisStory = story.add(mount).add(temple.add(buddhistMonk).add(childBuddhistMonk));
        // 遍历查看
        IComponent.lookUpComponent(thisStory, 0);

        System.out.println(" ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~");

        // 组织关系
        Composite ali = new Composite("阿里总部");
        Composite hz = new Composite("杭州服务部");
        Composite sh = new Composite("上海营销部");
        Composite bj = new Composite("北京技术部");

        Composite hz01 = new Composite("电商服务部");
        Composite hz02 = new Composite("营销服务部");

        Composite sh01 = new Composite("营销策划部");
        Composite sh02 = new Composite("营销推广部");

        Composite bj01 = new Composite("秒杀技术组");
        Composite bj02 = new Composite("抢购技术组");
        Composite bj03 = new Composite("推荐平台组");

        ali.add(hz.add(hz01).add(hz02))
            .add(sh.add(sh01).add(sh02))
            .add(bj.add(bj01).add(bj02).add(bj03));

        IComponent.lookUpComponent(ali, 0);
    }
}

4)测试结果

合成模式,也称为组合模式,通过类型自身的组合关系表达了部分-整体的逻辑结构,上述代码运行结果:

Composite: 开始讲故事...
----Leaf: 从前有座山
----Composite: 山里有座庙
--------Leaf: 庙里有个老和尚
--------Leaf: 庙里有个小和尚
 ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~
Composite: 阿里总部
----Composite: 杭州服务部
--------Leaf: 电商服务部
--------Leaf: 营销服务部
----Composite: 上海营销部
--------Leaf: 营销策划部
--------Leaf: 营销推广部
----Composite: 北京技术部
--------Leaf: 秒杀技术组
--------Leaf: 抢购技术组
--------Leaf: 推荐平台组

3、 Python实现

"""
设计模式——组合模式
组合模式(Composite Pattern):将对象组合成成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性.
"""

# 抽象一个组织类
class Component(object):

    def __init__(self, name):
        self.name = name

    def add(self,comp):
        raise NotImplementedError("该方法必须在子类中实现")

    def remove(self,comp):
        raise NotImplementedError("该方法必须在子类中实现")

    def lookup(self, depth):
        raise NotImplementedError("该方法必须在子类中实现")

# 叶子节点
class Leaf(Component):

    def add(self,comp):
        print('不能添加下级节点')

    def remove(self,comp):
        print('不能删除下级节点')

    def lookup(self, depth):
        strtemp = ''
        for i in range(depth):
            strtemp += strtemp + '-'
        print strtemp+self.name


# 枝节点
class Composite(Component):

    def __init__(self, name):
        self.name = name
        self.children = list()

    def add(self,comp):
        self.children.append(comp)

    def remove(self,comp):
        self.children.remove(comp)

    def lookup(self, depth):
        strtemp = ''
        
        for i in range(depth):
            strtemp += strtemp+'-'
        print(strtemp + self.name)
        
        for comp in self.children:
            comp.display(depth + 2)

if __name__ == "__main__":
    #生成树根
    root = Composite("root")
    #根上长出2个叶子
    root.add(Leaf('leaf A'))
    root.add(Leaf('leaf B'))

    #根上长出树枝Composite X
    comp = Composite("Composite X")
    comp.add(Leaf('leaf XA'))
    comp.add(Leaf('leaf XB'))
    root.add(comp)

    #根上长出树枝Composite X
    comp2 = Composite("Composite XY")
    #Composite X长出2个叶子
    comp2.add(Leaf('leaf XYA'))
    comp2.add(Leaf('leaf XYB'))
    root.add(comp2)
    # 根上又长出2个叶子,C和D,D没张昊,掉了
    root.add(Leaf('Leaf C'))
    leaf = Leaf("Leaf D")
    root.add(leaf)
    root.remove(leaf)
    #展示组织
    root.lookup(1)

4、 Go实现

1) 合成/组合模式:抽象接口

type IComponent interface {
    Name() string
    Description() string
    Price() float32
    LookUp()

    Add(IComponent)
    Remove(int)
    Child(int) IComponent
}

2) Leaf组件实现

type Leaf struct {
    name        string
    description string
    price       float32
}

func Leaf(name, description string, price float32) IComponent {
    return &Leaf{
        name:        name,
        description: description,
        price:       price,
    }
}

func (m *Leaf) Name() string {
    return m.name
}

func (m *Leaf) Description() string {
    return m.description
}

func (m *Leaf) Price() float32 {
    return m.price
}

func (m *Leaf) Lookup() {
    fmt.Printf("  %s, ¥%.2f\n", m.name, m.price)
    fmt.Printf("    -- %s\n", m.description)
}

func (m *Leaf) Add(IComponent) {
    panic("树叶节点,不包含该方法")
}

func (m *Leaf) Remove(int) {
    panic("树叶节点,不包含该方法")
}

func (m *Leaf) Child(int) IComponent {
    panic("树叶节点,不包含该方法")
}

3) Composite实现

type Composite struct {
    name        string
    description string
    children    []IComponent
}

func NewMenu(name, description string) IComponent {
    return &Composite{
        name:        name,
        description: description,
    }
}

func (m *Composite) Name() string {
    return m.name
}

func (m *Composite) Description() string {
    return m.description
}

func (m *Composite) Price() (price float32) {
    for _, v := range m.children {
        price += v.Price()
    }
    return
}

func (m *Composite) Print() {
    fmt.Printf("%s, %s, ¥%.2f\n", m.name, m.description, m.Price())
    fmt.Println("------------------------")
    for _, v := range m.children {
        v.Print()
    }
    fmt.Println()
}

func (m *Composite) Add(c IComponent) {
    m.children = append(m.children, c)
}

func (m *Composite) Remove(idx int) {
    m.children = append(m.children[:idx], m.children[idx+1:]...)
}

func (m *Composite) Child(idx int) IComponent {
    return m.children[idx]
}

4) 关于GO

其实更多时候Go语言的定位不是如此繁复的业务处理逻辑,而是数据结构的直观描述,类似合成设计模式这样的结构模式,在Go语言中应该直接通过组合的方式将不同的数据进行规范定义即可,没有必要为赋新词强说愁,没有那么多接口没有那么多继承,只是简单的通过不同复杂数据的组合方式完成即可。这一点上,上述代码就显得非常不友好了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • 1.初识组合模式 将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具...
    王侦阅读 1,053评论 0 2
  • 本文的主要内容: 介绍组合模式 示例 组合模式总结 源码分析组合模式的典型应用java.awt中的组合模式Java...
    小旋锋的简书阅读 1,017评论 0 4
  • 0.提前说明 模式选择的方法1)模式的功能——看是否能解决问题2)模式的本质——看模式是否主要用来解决这类问题3)...
    王侦阅读 1,033评论 0 1
  • 【学习难度:★★★☆☆,使用频率:★★★★☆】直接出处:组合模式梳理和学习:https://github.com/...
    BruceOuyang阅读 980评论 0 1
  • "书是人生的益友,但也仅止于此,人生的路还得自己走。也许有的人对一本书或一位作家一见倾心,爱之弥笃,乃至白头偕老,...
    蕙心缘阅读 408评论 1 2