• 设计模式-组合模式


    最后更新日期: 2017-12-10

    引言

    最近一直在看《Head First 设计模式》一书,此篇文章是基于 “第九章-迭代器与组合模式”, 我将此节分为两个部分: 迭代器、组合模式。

    强烈推荐此书。


    什么是组合模式?

    定义:
    允许你讲对象组合成树形结构来表现“整体/部分”层次结构。 组合能让客户以一致的方式处理个别对象以及对象组合。

    其实生活中有很多组合的例子:

    • 文件管理系统: 文件管理系统就是典型的组合模式。 存在一个根目录,然后目录底下可能是文件、也可能是文件夹,组合起来,就构成了文件的管理。
    • 公司的体制: 现在公司的体制也有点像组合. 公司有这一层层领导, 领导下面有各自团队,团队底下有项目组等,最后到底下的执行人员, 这种体制也很像一种树形的结构。

    用 swift 来设计组合模式,需要理解其对应关系

    • Component: 组合之中的接口的声明,定义 Leaf 与 Composite 的公共行为的接口, 对应 swift 中的 protocol;
    • Leaf(树叶): 表示叶节点的对象, 不能存储其他对象. 在文件管理系统中表示每一个具体的文件;
    • Composite(组件): 可以用来存储 Leaf 对象, 也可以存储 Composite 对象. 在文件管理系统中, 可表示每一个文件夹。

    有时候,为了方便处理,你也可以将 Leaf 理解为 没有子节点的 Composite


    组合模式设计菜单

    需求:

    现在有如下的需求:

    1. 设计早中晚菜单,
    • 早餐包括: 包子、馒头、鸡蛋、粽子等;
    • 午餐包括: 小鸡炖蘑菇、 大白菜、 卷心菜、羊肉等, 而且午餐还包括水果: 西瓜、香蕉、橙子等;
    • 晚餐: 白酒、大虾等

    对菜单设计需要更改合适,添加以及删除都需要比较方便。

    分析

    这种典型的树形结构图,可以利用组合模式来设计。 可以根据swift 的 协议扩展特效,和适合的设计出对应的模型

    源码

    Component <-> MenuComponet

    
    /** 
    name 与 display() 是遵守次协议必须实现的
    利用协议扩展,可以默认实现协议声明的内容
    */
    protocol MenuComponet  {
        
        var name : String { get }
        var price : Double? { get }
        var isVegetarian : Bool { get }
        
        func display()
        func add(_ comp :  MenuComponet)
        func remove(_ comp : MenuComponet)
    }
    
    
    extension MenuComponet {
        
        var price : Double? {  return nil }
        var isVegetarian : Bool { return false }
        
        func add(_ comp :  MenuComponet) {}
        func remove(_ comp : MenuComponet) {}
    }
    

    Leaf <-> MenuItem

    /**
    这是每一个具体的菜单类,必须有价格金额等信息
    当然,此处也是可以用struct来实现
    */
    
    class  MenuItem : MenuComponet {
        var name: String
        var price: Double
        var isVegetarian: Bool
        
        init(name: String, price: Double, isVegetarian: Bool) {
            self.name = name
            self.price = price
            self.isVegetarian = isVegetarian
        }
        
        func display() {
            print("(name) price is (price)")
        }
    }
    
    

    Composite <-> Menu

    /**
     相当于惨淡类型,早餐,午餐,水果、晚餐的类型
     它可以包含子的 Menu, 也可以包含 MenuItem
    */
    class Menu : MenuComponet {
    
        var name: String
        
        var subComps = [MenuComponet]()
    
        init(with name : String) {
            self.name = name
        }
        
        func add(_ comp: MenuComponet) {
            subComps.append(comp)
        }
        
        func remove(_ comp: MenuComponet) {
            if let index = subComps.index(where: { (comp1) -> Bool in
                return comp.name == comp1.name
            }) {
                subComps.remove(at: index)
            }
        }
        
        func display() {
            print(name)
            subComps.forEach { $0.display()}
        }
    }
    

    在 客户端(Client) 调用的时候, 我们可以实现对应的方法, 例如 display()

    class Waiter {
        
        var allMenus = [MenuComponet]()
        
        func initial() -> Self {
            
            let breakFastMenu = Menu(with: "早餐")
            let bun = MenuItem(name: "包子", price: 2.5, isVegetarian: false)
            let steamedBread =  MenuItem(name: "馒头", price: 1, isVegetarian: false)
            let egg = MenuItem(name: "鸡蛋", price: 1.5, isVegetarian: false)
            let zongzi = MenuItem(name: "粽子", price: 3, isVegetarian: true)
            breakFastMenu.add(bun)
            breakFastMenu.add(steamedBread)
            breakFastMenu.add(egg)
            breakFastMenu.add(zongzi)
            
            
            let lunchMenu = Menu(with: "午餐")
            let chickenSoup = MenuItem(name: "小鸡炖蘑菇", price: 25, isVegetarian: false)
            let cabbage =  MenuItem(name: "大白菜", price: 10, isVegetarian: true)
            let spinach = MenuItem(name: "卷心菜", price: 8, isVegetarian: true)
            let lamb = MenuItem(name: "羊肉", price: 20, isVegetarian: false)
            lunchMenu.add(chickenSoup)
            lunchMenu.add(cabbage)
            lunchMenu.add(spinach)
            lunchMenu.add(lamb)
            
            let lunchWithFruit = Menu(with: "水果")
            let watermelon = MenuItem(name: "西瓜", price: 10, isVegetarian: false)
            let orange =  MenuItem(name: "橘子", price: 8, isVegetarian: false)
            let banana =  MenuItem(name: "香蕉", price: 5, isVegetarian: false)
            lunchWithFruit.add(watermelon)
            lunchWithFruit.add(orange)
            lunchWithFruit.add(banana)
            
            lunchMenu.add(lunchWithFruit)
            
            let dinner = Menu(with: "晚餐")
            let wine = MenuItem(name: "高级白酒", price: 100, isVegetarian: false)
            let shrimp =  MenuItem(name: "油焖大虾", price: 40, isVegetarian: false)
            dinner.add(wine)
            dinner.add(shrimp)
    
            allMenus.append(breakFastMenu)
            allMenus.append(lunchMenu)
            allMenus.append(dinner)
            
            return self
        }
        
        // 此处仅仅设计了一个 display 的功能, 你可以添加更多功能
        func display() {
            allMenus.forEach { $0.display() }
        }
    }
    

    组合模式其实不是很复杂, 类似树形结构的都可以用组合模式来实现(App中的badge系统也可以如此设计)。

  • 相关阅读:
    C#总结(四)调用C++动态库
    Golang 入门系列(十二)ORM框架gorm
    《关键对话》如何高效沟通,营造无往不利的事业和人生?
    Golang 入门系列(十一)Go语言实现webapi
    Golang 入门系列(十) mysql数据库的使用
    Golang 入门系列(九) 如何读取YAML,JSON,INI等配置文件
    Golang 入门系列(八) cron定时任务
    Golang 入门系列(七) Redis的使用
    福利 | 互联网产品经理学习资料免费下载(可下载)
    福利 | 2018各大技术大会资料汇总(可下载)
  • 原文地址:https://www.cnblogs.com/gaox97329498/p/12070041.html
Copyright © 2020-2023  润新知