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

2020-08-28  本文已影响0人  默默_David

结构型设计模式

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

适配器模式

角色分析:

例如,在我们常见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
    }
}

桥接模式

下面是一个简单的例子,程序员作为抽象部分,程序员的工作内容为具体抽象部分,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,典型的树形结构。

角色分析:

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)

下面的案例中,

//抽象组件:手机
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)

系统类使用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,如果有则重用,没有就创建一个,这就是享元模式。

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

代理模式

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

//目标接口,代购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() {
        
    }
}
上一篇下一篇

猜你喜欢

热点阅读