设计模式-结构型(代码片段)

极客学伟 极客学伟     2023-03-03     578

关键词:

设计模式-结构型

结构型设计模式包含:代理模式、适配器模式、桥接模式、装饰模式、外观设计模式、享元模式、组合模式

代理模式

核心是在具体的功能类与使用者之间建立一个中介类作为代理,使用者通过代理对象对真实的功能类进行访问。 在iOS开发中,代理设计模式非常有用,在UIKit框架中,UITableViewUITextView 等组件的渲染和交互都采用了代理设计模式。

以病人预约看病的软件设计举例,核心功能类只有两个医生类和病人类,病人看病前首先预约,预约完成后问诊,医生陈述病情,然后开药。整个系统中有些行为既不属于病人类也不属于医生类,如医生的预约和问诊过程的控制等,这时就需要一个代理类代理医生处理这些行为。

重构后

class Patient 
    func describeCondition() -> String 
        let describe = "描述病情"
        print(describe)
        return describe
    

class Doctor 
    func writPrescription(condition: String) -> String 
        let prescription = "依据病情: \\(condition), 开的处方"
        print(prescription)
        return prescription
    

class DoctorProxy 
    var patient: Patient
    init(patient: Patient) 
        self.patient = patient
    
    func seeDoctor() 
        // 预约医生
        let doctor = reservation()
        // 病人描述病情
        let condition = self.patient.describeCondition()
        // 医生开处方
        doctor.writPrescription(condition: condition)
    
    func reservation() -> Doctor 
        let doctor = Doctor()
        print("预约医生")
        return doctor
    

let patient = Patient()
let doctorProxy = DoctorProxy(patient: patient)
doctorProxy.seeDoctor()

其中,病人并没有和医生进行直接交互,而是通过中间的代理类 DoctorProxy。实际开发中,使用代理设计模式可以使具体的功能类的聚合性更强,并可以在某些功能的执行前后进行额外的准备工作和善后工作。

适配器模式

适配器模式并不是软件设计中的最佳实践,其主要为了解决软件开发过程中新旧模块不兼容的问题。其定义:将一个类的接口转换成使用者期望的另外接口,使得原本接口不兼容的类可以一起工作。

当数据模型版本升级时,可以使用适配器模式兼容旧的数据模型

重构后

class User 
    var name: String
    var age: Int

class UserV2 
    var nickName: String
    var age: Int
    var address: String

class UserAdapter 
    static func toUserV2(user: User) -> UserV2 
        return UserV2(nickName: user.name, age: user.age, address: "")
    

let user = User(name: "学伟", age: 18)
let userV2 = UserAdapter.toUserV2(user: user)
print(userV2)

实际开发中,由于数据模型升级造成的代码不兼容问题会经常遇到,当项目过于庞大时,如果贸然修改以往的旧代码,会有很大的工作量,同时也会伴随很大的风险,使用适配器模式就是一种比较适合的折中选择。

桥接模式

桥接模式是合成复用原则的一种应用,其核心是将抽象与实现分离,用组合来代替继承关系,从而给类更多的扩展性,降低类之间的耦合度。 实际开发中,当某个类具有多维度的属性时,在组织类的结构时,使用桥接模式十分适合。 例如:汽车从功能上分为轿车和卡车,颜色上又分为黑色白色。在设计时有两种设计方案:一种是创建轿车和卡车的类,每个类包含颜色属性:

enum Color 
    case red
    case green

class Car 
    var color: Color

class Saloon: Car 
    print("我是轿车")

class Truck: Car 
    print("我是卡车")

另外一种设计方案可以根据桥接模式,根据实际需要对功能和颜色进行组合。

重构后

enum Color 
    case red
    case green

enum CarType 
    case saloon
    case truck
    var name: String 
        switch self 
        case .saloon:
            return "轿车"
        case .truck:
            return "卡车"
        
    

protocol CarProtocol 
    var color: Color  get 
    var carType: CarType  get 
    func log()

extension CarProtocol 
    func log() 
        print("我是" + carType.name)
    

class Car: CarProtocol 
    var color: Color
    var carType: CarType
    init(color: Color, carType: CarType) 
        self.color = color
        self.carType = carType
    

let car = Car(color: .red, carType: .saloon)
car.log()

通过组合颜色和类型两个枚举来构建汽车对象,避免了因继承带来的耦合问题。

装饰模式

在不改变对象结构的情况下,为该对象增加一些功能。 类比现实生活中的:手机壳、壁画...

以为墙添加贴纸的逻辑设计为例:

重构后

protocol WallProtocol 
    func printInfo()

class Wall: WallProtocol 
    func printInfo() 
        print("墙面")
    

class StickerDecorator: WallProtocol 
    var wall: Wall
    init(wall: Wall) 
        self.wall = wall
    
    func printInfo() 
        print("贴纸装饰")
        self.wall.printInfo()
    

let wall = Wall()
let stickerDecorator = StickerDecorator(wall: wall)
stickerDecorator.printInfo()

其中 StickerDecorator 即装饰器,也需要完整的实现功能类所实现的接口,这样才能不会改变被装饰对象的原始行为。 使用装饰模式可以理解成:为对象的行为进行扩展,只是相比较于继承,装饰模式更加灵活、类之间的耦合度也更低。同时,装饰模式可能由于过度设计而增加过多装饰器类,使系统复杂性变高。

外观设计模式

在软件设计中,当一个系统的功能越来越强时,子模块会越来越多,应用端对系统的访问也会越来越复杂。这时可以通过提供一个外观类来统一处理这些交互,降低应用端使用的复杂性。 以客户购买商品流程的设计为例:

struct User 
    var name: String

struct Goods 
    static func choseGoods(user: User) 
        print("\\(user.name)选择商品")
    

struct Cashier 
    static func pay(user: User) 
        print("\\(user.name)付款")
    

struct Package 
    static func packing(user: User) 
        print("\\(user.name)打包")
    

let user = User(name: "学伟")
Goods.choseGoods(user: user)
Cashier.pay(user: user)
Package.packing(user: user)

User需要完成一个购物流程需要同时与 GoodsCashierPackage 三个类进行交互。当每个模块都变得越来越复杂时,代码的扩展和维护将变得十分困难。 对于这样的场景,可以定义一个外观类来统一处理用户的购物逻辑。

重构后

...
struct Store 
    static func shop(user: User) 
        Goods.choseGoods(user: user)
        Cashier.pay(user: user)
        Package.packing(user: user)
    

let user = User(name: "学伟")
Store.shop(user: user)

其中,Store 起到外观的作用,顾客只需要与 Store 一个类进行交互即可,

享元模式

运用共享技术实现大量细粒度对象的复用,避免大量重复对象造成系统的资源开销。 在享元模式中,需要根据共享性将对象中的数据拆分成内部状态和外部状态,之后将内部状态封装成享元对象用户共享。享元模式会增加系统的复杂度,对于不会产生大量重复对象的系统并不适用。

以黑白棋设计为例:

struct Place 
    var x: Int
    var y: Int

enum Color 
    case White
    case Black

class ChessPiece 
    var place: Place
    var color: Color
    var radius: Double
    init(place: Place, color: Color, radius: Double) 
        self.place = place
        self.color = color
        self.radius = radius
    

一个棋子除了位置不同外,颜色和半径对于大部分棋子来说是相同的,这种场景下,place 就是 外部状态,color与radius为内部状态,可以使用享元模式重构

重构后

struct Place 
    var x: Int
    var y: Int

enum Color 
    case White
    case Black

class ChessPieceFlyweight 
    var color: Color
    var radius: Double
    init(color: Color, radius: Double) 
        self.color = color
        self.radius = radius
    

class ChessPieceFlyweightFactory 
    static let white = ChessPieceFlyweight(color: .White, radius: 16.0)
    static let black = ChessPieceFlyweight(color: .Black, radius: 16.0)
    static func getChessPieceFlyweight(color: Color) -> ChessPieceFlyweight 
        switch color 
        case .White:
            return white
        case .Black:
            return black
        
    

class ChessPiece 
    var place: Place
    var chessPieceFlyweight: ChessPieceFlyweight
    init(place: Place, color: Color) 
        self.place = place
        self.chessPieceFlyweight = ChessPieceFlyweightFactory.getChessPieceFlyweight(color: color)
    

即便创建若干个棋子,真实的 ChessPieceFlyweight 只有两个,随着创建的个数越多,节省的内存也越多。

组合模式

采用树状层级结构来表示部分与整体的关系,使得无论是整体对象还是单个对象,对其访问都具有一致性。 在面向对象设计思想中,完整的文件系统至少需要两个类来描述,文件夹和文件;文件系统实际就是树状层级结构,可以使用组合模式设计。

重构后

enum NodeType 
    case Folder
    case File

protocol FileNode 
    var type: NodeType  get 
    var name: String  get 
    func addNode(node: FileNode)
    func removeNode(node: FileNode)
    func getAllNode() -> [FileNode]

class file: FileNode 
    var type: NodeType
    var name: String
    var child = [FileNode]()
    init(type: NodeType, name: String) 
        self.type = type
        self.name = name
    
    func addNode(node: FileNode) 
        self.child.append(node)
    

    func removeNode(node: FileNode) 
        self.child = self.child.filter( n in
            if node.name == n.name && node.type == n.type 
                return false
            
            return true
        )
    

    func getAllNode() -> [FileNode] 
        return self.child
    

通过定义统一的 FileNode 接口,使得使用方无论关心当前操作的节点是文件夹还是文件,都有统一的访问方式,而且屏蔽了树结构中层级的概念,这是组合模式最大的优势。

csharp设计模式-单例-结构代码(代码片段)

查看详情

csharp设计模式-原型-结构代码(代码片段)

查看详情

设计模式之结构型模式(代码片段)

在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。对象结构的设计很容易体现出... 查看详情

组合模式---结构型模式(代码片段)

组合模式(一般用于树状结构,其实有点想发布订阅模式) 以文件夹系统为列子,假如我们要查杀D盘下的文件 文件夹对象和文件对象都有查杀方法,而文件夹对象还有增、删、查的功能publicinterfaceAdFilevoidkill();interfaceFlo... 查看详情

23种设计模式介绍----结构型模式(代码片段)

由于设计模式篇幅比较大,如果在一篇文章讲完所有的设计模式的话不利于阅读。于是我把它分为三篇文章23种设计模式介绍(一)----创建型模式23种设计模式介绍(二)----结构型模式23种设计模式介绍(三)----行为型模式  ... 查看详情

csharp设计模式-抽象工厂-结构代码(代码片段)

查看详情

csharp设计模式-工厂方法-结构代码(代码片段)

查看详情

python设计模式---结构型之门面模式(代码片段)

门面,系统,客户端~fromdjango.testimportTestCaseclassHotelier:def__init__(self):print(‘ArrangingtheHotelforMarriage?---‘)def_is_available(self):print(‘IstheHotelfreefortheeventongivendat?‘)returnTruedefbook_ho 查看详情

go中设计模式之结构型模式(代码片段)

外观模式1.定义:外部与一个子系统通信必须通过一个统一的对象进行,为子系统中的一组接口提供一致界面。2.代码示例://定义对外APItypeAPIinterfaceTest()funcNewAPI()APIreturnapiImplnewMod()typeapiImplstructmmodfunc(aapiImpl)Test()a.m.mod()//需要交... 查看详情

markdownjs结构设计模式(代码片段)

查看详情

设计模式-结构型模式_适配器模式(代码片段)

文章目录结构型模式概述Case场景模拟⼯程BadImplBetterImpl(适配器模式重构代码)MQ消息适配接口适配小结结构型模式结构型模式主要是解决如何将对象和类组装成较大的结构,并同时保持结构的灵活和⾼效。结构型模... 查看详情

结构型设计模式(代码片段)

架构型设计模式成员门面模式代理模式装饰器模式组合模式享元模式桥接模式适配器模式1.代理模式1.1定义为其他对象提供一种代理以控制对这个对象的访问解决问题:在直接访问对象时带来的问题,比如说:要访问的对象在远... 查看详情

设计模式-结构型模式_适配器模式(代码片段)

文章目录结构型模式概述Case场景模拟⼯程BadImplBetterImpl(适配器模式重构代码)结构型模式结构型模式主要是解决如何将对象和类组装成较大的结构,并同时保持结构的灵活和⾼效。结构型模式包括:适配器、桥... 查看详情

csharp设计模式-生成器-结构代码(代码片段)

查看详情

设计模式-结构型模式_桥接模式(代码片段)

文章目录结构型模式概述CaseBadImplBetterImpl小结结构型模式结构型模式主要是解决如何将对象和类组装成较大的结构,并同时保持结构的灵活和⾼效。结构型模式包括:适配器、桥接、组合、装饰器、外观、享元、代理ÿ... 查看详情

设计模式-结构型模式_桥接模式(代码片段)

文章目录结构型模式概述CaseBadImplBetterImpl小结结构型模式结构型模式主要是解决如何将对象和类组装成较大的结构,并同时保持结构的灵活和⾼效。结构型模式包括:适配器、桥接、组合、装饰器、外观、享元、代理ÿ... 查看详情

结构型模式之装饰模式(代码片段)

装饰模式(DecoratorPattern)是一种比较常见的模式。定义:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。装饰模式类图如下所示。 装饰模式有以下4个角色。抽象构件(Component)角... 查看详情

无废话设计模式(10)结构型模式--外观模式(代码片段)

0-前言  外观模式定义:为子系统中的一组接口提供一个一致的界面,此模式定了一个高层接口          这一接口使得这一子系统更加容易使用;1-实现1-1、简单UML图:  1-2、代码实现//1、子系统A(研... 查看详情