美文网首页
iOS设计模式(二)、结构型设计模式

iOS设计模式(二)、结构型设计模式

作者: 默默_David | 来源:发表于2020-08-28 16:28 被阅读0次

    结构型设计模式

    结构型设计模式主要用于处理类和对象的组合

    适配器模式

    • 将一个原始接口转成客户端需要的接口
      • 原始接口--相当于Adaptee
      • 客户端需要接口(调用者) --- 相当于Target
    • 原始接口不兼容现在新的接口,让它们两个可以一起工作
      • 一起工作需要Adapter实现

    角色分析:

    • 适配器
    • 目标接口
    • 被适配者

    例如,在我们常见UITableView代码中:
      适配器是ViewController(实现两个协议)
      目标接口是UI界面->UITableView(Cell)
      被适配者是数据(Model)

    这种时候,UIViewController中的代码会非常臃肿。这时候,我们可以创建一个Adapter类来做适配器,将tableView的dataSource和delegate赋值给Adapter对象,在Adapter中实现协议,完成数据的交互。 这样,我们就将ViewController和Model以及View都进行分离。而且,如果我们要在ViewController中关心cell的点击事件,也可以通过适配器传递。

    适配器分为类适配器和对象适配器。

    类适配器

    类适配器中适配器继承于被适配者,适配器方法中调用父类的方法进行适配

    如下例中,适配器Adapter将adaptee中的美元适配为人民币

    //被适配者
    class Adpatee{
        var dollar: Double{
            1000.0
        }
    }
    //目标接口
    protocol Target{
        var RMB: Double{ get }
    }
    //适配器
    class Adapter: Adpatee, Target{
        var RMB: Double{
            dollar * 7.0
        }
    }
    
    对象适配器

    对象适配器中,Adapter遵守了Target协议,拥有一个adaptee的引用,当调用Adpater的适配方法时,内部实现对adaptee的方法进行适配。

    如下例中,适配器Adapter将adaptee中的美元适配为人民币

    //被适配者
    class Adpatee{
        var dollar: Double{
            return 1000.0
        }
    }
    //目标接口
    protocol Target{
        var RMB: Double{ get }
    }
    //适配器
    class Adapter: Target{
        init(_ adaptee: Adpatee){
            self.adaptee = adaptee
        }
        var adaptee: Adpatee
        var RMB: Double{
            adaptee.dollar * 7.0
        }
    }
    

    桥接模式

    • 定义
      将抽象部分与实现部分分离,使它们都可以独立的变化。
    • 解决问题:
      类层级爆炸问题
    • 使用场景:
      • 开发中面临层级结构复杂(爆炸),可以使用桥接模式
      • 对不同的API之间进行桥接
      • 一个类存在两个独立的维度,且这两个维度都需要进行扩张(至少是两个维度)
    • 角色划分
      • 抽象部分
      • 实现抽象部分
      • 具体部分
      • 具体实现部分

    下面是一个简单的例子,程序员作为抽象部分,程序员的工作内容为具体抽象部分,iOS、安卓、java程序员为具体部分,iOS、安卓、java工作内容为具体实现部分。

    //程序员,抽象部分
    protocol ComputerProgrammer{
        init(_ op: WorkOperation)
        var op: WorkOperation{ get }
        func work()
    }
    //具体工作(做什么),实现抽象部分
    protocol WorkOperation{
        func operation()
    }
    
    //MARK: 具体抽象部分
    class iosProgrammer: ComputerProgrammer{
        required init(_ op: WorkOperation) {
            self.op = op
        }
        var op: WorkOperation
        func work() {
            print("ios工程师开始工作")
            op.operation()
        }
    }
    class androidProgrammer: ComputerProgrammer {
        required init(_ op: WorkOperation) {
            self.op = op
        }
        var op: WorkOperation
        func work() {
            print("安卓工程师开始工作")
            op.operation()
        }
    }
    class javaProgrammer: ComputerProgrammer {
        required init(_ op: WorkOperation) {
            self.op = op
        }
        var op: WorkOperation
        func work() {
            print("Java工程师开始工作")
            op.operation()
        }
    }
    
    //MARK: 具体实现部分
    class iosOperation: WorkOperation{
        func operation() {
            print("使用OC或Swift开发")
        }
    }
    
    class androidOperation: WorkOperation{
        func operation() {
            print("使用java或kotlin开发")
        }
    }
    
    class javaOperation: WorkOperation{
        func operation() {
            print("使用java开发")
        }
    }
    
    
    let ios = iosProgrammer(iosOperation())
    ios.work()
    let android = androidProgrammer(androidOperation())
    android.work()
    let java = javaProgrammer(javaOperation())
    java.work()
    
    打印结果:
    ios工程师开始工作
    使用OC或Swift开发
    安卓工程师开始工作
    使用java或kotlin开发
    Java工程师开始工作
    使用java开发
    

    组合模式(Composite)

    将对象组合成树形结构以表示部分-整体的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性,用户可以统一的使用组合结构中的所有对象。

    总结:使得用户对单个对象和组合最新的使用具有一致性。

    最简单的最常见的例子就是UIView,典型的树形结构。

    角色分析:

    • 抽象根节点(Component)
    • 具体子节点(Composite)
    • 叶子结点(Leaf)

    Composite、Leaf指一类结点,并不是唯一,其中Leaf是无子结点(叶子结点)。

    使用场景:
    只允许使用,不允许继承。

    组合模式最大的优点是它的结点可以自由增加,且调用结点方便。

    //Component
    @objc protocol TreeProtocol{
        var trees: Array<TreeProtocol> { get }
        
        func doSomething()
        @objc optional func addChild(_ child: TreeProtocol)
        @objc optional func removeChild(_ index: Int)
        @objc optional func getChildren(_ index: Int) -> TreeProtocol
        @objc optional func clear()
    }
    
    //Composite
    class Tree: TreeProtocol{
        var trees: Array<TreeProtocol>
        
        init(){
            trees = Array<TreeProtocol>()
        }
        
        func doSomething() {
            
        }
        
        func addChild(_ child: TreeProtocol) {
            trees.append(child)
        }
        func removeChild(_ index: Int) {
            trees.remove(at: index)
        }
        func getChildren(_ index: Int) -> TreeProtocol {
            trees[index]
        }
        func clear() {
            trees.removeAll()
        }
        
    }
    
    //Leaf
    class Leaf: TreeProtocol{
        var trees: Array<TreeProtocol>
        init(){
            trees = Array<TreeProtocol>()
        }
        
        func doSomething() {
            
        }
    }
    
    

    装饰模式(Decorator)

    • 定义
      动态地给一个对象添加一些额外的职能
    • 场景
      需要透明且动态地扩展类的功能时
    • 角色分析
      • 抽象组件
      • 具体组件
      • 抽象装饰者
      • 具体装饰者

    下面的案例中,

    • 抽象组件是手机
    • 具体组件是iPhone11
    • 抽象装饰者是手机壳
    • 具体装饰者
      • 质量好的手机壳:耐磨、防水、防尘...(300)
        • 耐磨:wearProof()
        • 防水:waterProof()
        • 防尘:dustProof()
          GoodShell
      • 质量差的手机壳:耐磨(50)
        • 耐磨:wearProof()
          PoorShell
    //抽象组件:手机
    protocol MobilePhone{
        //装饰->手机壳
        func shell()
    }
    //具体组件:iPhone11
    class Iphone11: MobilePhone {
        func shell() {
            print("苹果11")
        }
    }
    //抽象装饰者
    class MobilePhoneShell: MobilePhone{
        private var mobile: MobilePhone
        
        init(_ mobile: MobilePhone) {
            self.mobile = mobile
        }
        func shell() {
            self.mobile.shell()
        }
    }
    //具体装饰者->好的手机壳
    class GoodShell: MobilePhoneShell{
        func wearProof(){
            print("耐磨功能")
        }
        func waterProof(){
            print("防水功能")
        }
        func dustProof(){
            print("防尘功能")
        }
    }
    //具体装饰者->差的手机壳
    class PoorShell: MobilePhoneShell{
        func wearProof(){
            print("耐磨功能")
        }
    }
    
    let phone = Iphone11()
    phone.shell()
    
    let good = GoodShell(phone)
    good.shell()
    good.dustProof()
    good.waterProof()
    good.wearProof()
    
    let poor = PoorShell(phone)
    poor.shell()
    poor.wearProof()
    
    //打印结果
    苹果11
    苹果11
    防尘功能
    防水功能
    耐磨功能
    苹果11
    耐磨功能
    

    动态地给一个对象添加一个额外的只能,就增加功能来说,Decorator模式相比生成子类更为灵活。

    在OC中,我们可以通过Category来实现装饰模式。虽然通过Category可以实现装饰模式,但是这并不是一个严格的实现,由类别添加的方法是编译时绑定的,而装饰模式是动态绑定的,另外类别也没有封装被扩展类的实例。类别适合装饰器不多的时候。

    外观模式(Facade)

    • 定义:门面模式
      • 要求一个子系统的外部和其内部的通信必须通过统一的对象进行
      • 外观模式提供了一个高层次接口,使得子系统更易于使用
    • 场景
      • 场景一:为复杂系统提供一个简单接口
      • 场景二:当我们构建一个层次结构的子系统时,使用Facede模式定义子系统中每一层入口点。如果子系统之间相互依赖,可以通过此模式简化它们之间的依赖关系。
    • 角色分析
      • 系统对外统一接口
      • 子系统接口

    系统类使用UIImagePickerController为拍照提供了高层次接口,而内部是使用AVFoundation封装的,这种都是外观模式的体现。

    下面案例我们使用手机来举例,手机提供了拍照、玩游戏、听音乐等高层次接口,而其内部都是调用各自的子系统来实现的。

    class Iphone {
        
        private var camera: CameraProtocol?
        private var game: GameProtocol?
        private var music: MusicProtocol?
        
        init(camera: CameraProtocol? = nil,game: GameProtocol? = nil,music: MusicProtocol? = nil) {
            self.camera = camera
            self.game = game
            self.music = music
        }
        
        //相机
        func cameraAction(){
            //启动相机,开始拍照,关闭相机
            self.camera?.open()
            self.camera?.takePicture()
            self.camera?.close()
        }
        //玩游戏
        func palyGame(){
            //启动游戏,开始游戏,关闭游戏
            self.game?.open()
            self.game?.startGame()
            self.game?.close()
        }
        //听音乐
        func palyMusic(){
            //启动音乐,开始听歌,关闭音乐
            self.music?.open()
            self.music?.startMusic()
            self.music?.close()
        }
    }
    //相机模块接口
    protocol CameraProtocol{
        func open()
        func takePicture()
        func close()
    }
    
    //具体实现类
    class CameraImpl: CameraProtocol{
        func open() {
            print("打开相机")
        }
        func takePicture() {
            print("拍照")
        }
        func close() {
            print("关闭相机")
        }
    }
    //游戏模块接口
    protocol GameProtocol{
        func open()
        func startGame()
        func close()
    }
    //游戏具体实现类
    class GameImpl: GameProtocol{
        func open() {
            print("启动游戏")
        }
        func startGame() {
            print("开始游戏")
        }
        func close() {
            print("关闭游戏")
        }
    }
    
    
    //音乐模块接口
    protocol MusicProtocol{
        func open()
        func startMusic()
        func close()
    }
    //具体音乐实现
    class MusicImpl: MusicProtocol {
        func open() {
            print("打开音乐")
        }
        func startMusic() {
            print("播放音乐")
        }
        func close() {
            print("关闭播放")
        }
    }
    

    享元模式(FlyWeight)

    • 定义
      使用共享对象可以有效的支持大量的细粒度对象,强调:对象共享
    • 场景
      • 场景一:系统存在大量的相似对象
      • 场景二:需要缓冲池场景
    • 角色分析
      • 享元对象接口
      • 具体享元对象
      • 享元工厂(负责创建对象、管理对象)

    例子:我们在一个界面上生成1000个花,花的种类一共六种(六种不同图片),如图所示


    我们不用享元模式代码如下:

    enum FlowerType: Int{
        case anemone,cosmos,gerberas,hollyhock,jasmine,zinnia,totalNumbersOfFlowerTypes
    }
    
    class ViewController: UIViewController{
        
        override func viewDidLoad() {
            super.viewDidLoad()
            for _ in 0 ..< 1000{
                autoreleasepool {
                    let screenBounds = view.bounds
                    let x = arc4random()%UInt32(screenBounds.width)
                    let y = arc4random()%UInt32(screenBounds.height)
                    
                    let minSize = 10
                    let maxSize = 50
                    
                    let WH = (Int(arc4random()) % (maxSize - minSize + 1)) + minSize
                    
                    let area = CGRect(x: Int(x), y: Int(y), width: WH, height: WH)
                    
                    let type = Int(arc4random()) % FlowerType.totalNumbersOfFlowerTypes.rawValue
                    
                    let imageView = flowerView(FlowerType(rawValue: type) ?? FlowerType.anemone)
                    imageView.frame = area
                    view.addSubview(imageView)
                }
            }
        }
        //根据类型获取
        func flowerView(_ type: FlowerType) -> UIImageView{
            var imageName = ""
            switch type {
            case .anemone:
                imageName = "anemone.png"
            case .cosmos:
                imageName = "cosmos.png"
            case .gerberas:
                imageName = "gerberas.png"
            case .hollyhock:
                imageName = "hollyhock.png"
            case .jasmine:
                imageName = "jasmine.png"
            case .zinnia:
                imageName = "zinnia.png"
            default:
                break
            }
            let image = UIImage(named: imageName)
            return UIImageView(image: image)
        }
    }
    

    如上面的代码所示,我们一共创建了1000个视图添加到view上,这样做的后果就是生成大量的对象,内存占用很大,特别图片如果也很大的话,进行绘制会耗费太多的GPU渲染。

    从代码上看,其实只有6种花放在不同的位置而已,那我们可以利用享元模式思想,复用这两种花,然后绘制到不同位置,而不是增加对象添加到视图上。

    enum FlowerType: Int{
        case anemone,cosmos,gerberas,hollyhock,jasmine,zinnia,totalNumbersOfFlowerTypes
    }
    
    //具体享元对象
    class FlowerView: UIImageView{
        override func draw(_ rect: CGRect) {
            image?.draw(in: rect)
        }
    }
    //享元工厂
    class FlowerFactory {
        //缓存池
        var flowerPool = [FlowerType : UIImageView]()
        //缓存池中获取,如果缓存池中没有,则创建一个
        func flowerView(_ type: FlowerType) -> UIImageView{
            
            var imageView = flowerPool[type]
            
            if imageView == nil {
                var imageName = ""
                switch type {
                case .anemone:
                    imageName = "anemone.png"
                case .cosmos:
                    imageName = "cosmos.png"
                case .gerberas:
                    imageName = "gerberas.png"
                case .hollyhock:
                    imageName = "hollyhock.png"
                case .jasmine:
                    imageName = "jasmine.png"
                case .zinnia:
                    imageName = "zinnia.png"
                default:
                    break
                }
                let image = UIImage(named: imageName)
                imageView = FlowerView(image: image)
                
                flowerPool[type] = imageView!
            }
            return imageView!
        }
    }
    
    class FlyWeightView: UIView{
        var flowerList: Array<[NSValue : UIImageView]>?
        override func draw(_ rect: CGRect) {
            guard let flowerList = flowerList else {
                return
            }
            for dic in flowerList {
                guard let key = dic.keys.first,let imageView = dic.values.first else {
                    continue
                }
                let area = key.cgRectValue
                imageView.draw(area)
            }
        }
    }
    
    class ViewController: UIViewController{
        
        override func viewDidLoad() {
            super.viewDidLoad()
            //使用享元模式
            let factory = FlowerFactory()
            
            var flowerList = Array<[NSValue:UIImageView]>()
            for _ in 0 ..< 1000{
                autoreleasepool {
                    let type = Int(arc4random()) % FlowerType.totalNumbersOfFlowerTypes.rawValue
                    //重复利用对象
                    let flowerView = factory.flowerView(FlowerType(rawValue: type) ?? FlowerType.anemone)
                    
                    let screenBounds = view.bounds
                    let x = arc4random()%UInt32(screenBounds.width)
                    let y = arc4random()%UInt32(screenBounds.height)
                    let minSize = 10
                    let maxSize = 50
                    let WH = (Int(arc4random()) % (maxSize - minSize + 1)) + minSize
                    let area = CGRect(x: Int(x), y: Int(y), width: WH, height: WH)
    
                    let key = NSValue(cgRect: area)
                    let dictionary = [key : flowerView]
                    flowerList.append(dictionary)
                }
            }
            
            let flyWeightView = FlyWeightView(frame: view.bounds)
            flyWeightView.flowerList = flowerList
            
            self.view = flyWeightView
        }
        
    }
    
    

    可以明显看到内存已经降下来了,我们只是生成了对象flowerView,但是并没有add到FlyweightView上,我们使用image重新绘制了一个新的位置去显示。

    tableViewCell的重用机制就是实现了享元模式,在要使用一个cell的时候,会先去重用池中查询是否有可重用的cell,如果有则重用,没有就创建一个,这就是享元模式。

    享元模式最主要有两个关键组件,可共享的享元对象和保存它们的享元池

    代理模式

    • 定义
      为其它对象提供一种代理,控制对这个对象的访问。
    • 角色分析
      • 代理对象
        • 持有目标对象引用
        • 实现目标接口
      • 目标接口
      • 具体目标对象
    • 变种:动态代理(Java)、静态代理

    比如我们让代购的人帮忙购买手机

    //目标接口,代购iPhone
    protocol PersonProtocol{
        //下单(选购)
        func buyProduct()
        //支付
        func payProduct()
    }
    
    //代理对象
    class Person{
        //代理
        var proxy: PersonProtocol?
        
        func buy(){
            proxy?.buyProduct()
        }
        func pay(){
            proxy?.payProduct()
        }
    }
    //具体目标对象
    class Proxy: PersonProtocol{
        func buyProduct() {
            
        }
        func payProduct() {
            
        }
    }
    

    相关文章

      网友评论

          本文标题:iOS设计模式(二)、结构型设计模式

          本文链接:https://www.haomeiwen.com/subject/vfilsktx.html