kotlin设计模式-结构型

62 阅读6分钟

装饰模式

  • 也叫包装模式

  • 动态的给一个对象添加一下额外的职责,就增加功能来说,装饰模式相比生成子类更为灵活

  • 使用一种对客户端透明的方式来动态的扩展对象的功能,也是继承关系的一种替代方案

  • 与代理模式的区别:装饰模式是为所装饰的对象增强功能;代理模式则是对代理的对象施加控制,但不对对象本身的功能进行增强;

角色

  1. Component:定义一个对象接口,可以给这些对象动态地添加职责。

  2. ConcreteComponent:定义一个对象,可以给这个对象添加一些职责。

  3. Decorator:维持一个指向 Component 对象的指针,并定义一个与 Component 接口一致的接口。

  4. ConcreteDecorator:向组件添加职责。

适用场景

  • 需要透明且动态的扩张类的功能时
  • 动态增加,动态撤销

实现方式

// 对象接口,用于定义对象最基本的操作
interface Person {
    fun decorate()
}

// 具体对象,也是被装饰者
class Man : Person{
    override fun decorate() {
        println("男人打扮")
    }
}

// 装饰着抽象类,维持一个指向被装饰者的指针,并继承对象接口,用于返回对象
abstract class Decorator(var person: Person) : Person{
    override fun decorate() {
        this.person.decorate()
    }
}

// 实际装饰者,对被装饰者进行实际的装饰操作
class ClothesDecorator(var mPerson: Person) : Decorator(mPerson){
    override fun decorate() {
        super.decorate()
        println("穿衣服")
    }
}

class TrousersDecorator(var mPerson: Person) : Decorator(mPerson){
    override fun decorate() {
        super.decorate()
        println("穿裤子")
    }
}

// 主方法用于调用
fun main() {
    val person = Man()
    val clothesDecorator = ClothesDecorator(person)
    val trousersDecorator = TrousersDecorator(clothesDecorator)
    trousersDecorator.decorate()
}

优缺点

优点

  • 动态扩展一个实现类的功能,装饰类和被装饰类可以独立发展,不会相互耦合

缺点

  • 多层装饰比较复杂

装饰模式与代理模式的区别

  1. 装饰器模式强调的是增强自身,在被装饰之后你能够在被增强的类上使用增强后的功能。增强后你还是你,只不过能力更强了而已;代理模式强调要让别人帮你去做一些本身与你业务没有太多关系的职责(记录日志、设置缓存)。代理模式是为了实现对象的控制,因为被代理的对象往往难以直接获得或者是其内部不想暴露出来。

  2. 装饰模式是以对客户端透明的方式扩展对象的功能,是继承方案的一个替代方案;代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用;

  3. 装饰模式是为装饰的对象增强功能;而代理模式对代理的对象施加控制,但不对对象本身的功能进行增强;

适配器模式

适配器模式将一个类的接口转换为客户期望的另一个接口,适配器让原本不兼容的接口可以合作的亲密无间

适用场景

1 系统需要使用现有的类,而这些类的接口不符合系统的需要。 2 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。 3 需要一个统一的输出接口,而输入端的类型不可预知。

实现方式

// 源数据
class AC220V {
    fun output() : Int{
        return 220
    }
}

// 适配器接口
interface PowerAdapter {
    fun doConvert() : Int
}

// 适配器实现
class HuaweiFastCharge(var aC220V: AC220V) : PowerAdapter{
    override fun doConvert(): Int {
        return aC220V.output()/22
    }
}

// 使用
fun main() {
    val powerAdapter = HuaweiFastCharge(AC220V())
    println("这是电压:${powerAdapter.doConvert()}")
}

优缺点

优点

提升了类的复用和灵活度。

缺点

使用过多,系统会比较杂乱,难以把握。

桥接模式

而桥接模式有两种理解方式:

  • 将抽象和实现解耦,让它们能独立开发;(应用场景较少)
  • 用抽象关联取代多层继承,将类间的继承关系转换为动态的对象组合关系; (用得较多,避免了多层继承类爆炸问题)

桥接模式可以说是DIP原则(依赖反转)的具体实践,从依赖一个大而全的对象 → 依赖两个可以独立变化的维度。

适用场景

桥接模式的一个常用使用场景就是为了替换 继承,继承本身具备强侵入性(父类代码侵入子类),造成子类臃肿,因此,优先使用组合/聚合的方式。

桥接模式的特点是将抽象与实现分离,因此它适合

  • 一个类存在两个或多个独立变化的维度,而且这两个维度都需要扩展
  • 不希望或不适用继承的场景

实现方式

// 颜色变化维度
interface IColor {
    fun draw() : String
}

// 具体的颜色对象
class red : IColor{
    override fun draw(): String {
        return "红色"
    }
}

// 形状变化维度
abstract class shape(var iColor: IColor){
    abstract fun show()
}

// 具体的形状变化实例
class circle(var mColor: IColor) : shape(mColor){
    override fun show() {
        println("这是 ${mColor.draw()} 的圆形")
    }
}

fun main() {
    val red = red()
    val circle = circle(red)
    println(circle.show())
}

优缺点

优点

分离实体与行为,更好的可扩展性,代替多层继承方案,极大减少子类数量

缺点

  • 增加设计难度:一开始就要针对抽象层进行,正确识别两个独立维度需要一定的经验积累;
  • 增加维护成本:组合和聚合不像继承那样容易找到对象简单的调用关系;
  • 导致性能下降:组合或聚合关系在OOP中使用委托的事项方式,调用对象变多,自然影响程序性能;

外观模式

外观(Facade)模式又叫作门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。

外观模式的本质是:封装交互,简化调用

使用场景

  • 对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。

  • 当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。

  • 当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。

使用方式

class AirCondition {
    fun on() {
        System.out.println("空调打开...");
    }

    fun off() {
        System.out.println("空调关闭...");
    }
}

class Light {
    fun on() {
        System.out.println("灯光打开...");
    }

    fun off() {
        System.out.println("灯光关闭...");
    }
}

public class SmartAppliancesFacade(val light: Light = Light(), val airCondition: AirCondition = AirCondition()) {

    //一键打开
    fun on() {
        light.on();
        airCondition.on();
    }

    //一键关闭
    fun off() {
        light.off();
        airCondition.off();
    }
}

fun main() {
    val light = Light()
    var airCondition = AirCondition()
    light.off()
    airCondition.off()

    val smartAppliancesFacade = SmartAppliancesFacade()
    smartAppliancesFacade.off()
}

优缺点

优点

  • 降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。

  • 对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。

  • 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象。

缺点

  1. 不能很好地限制客户使用子系统类,很容易带来未知风险。

  2. 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则”。

享元模式

享元模式把一个对象的状态分成内部状态和外部状态,内部状态即是不变的,外部状态是变化的,然后通过共享不变的部分,达到减少对象数量并节约内存的目的。

享元模式的本质是缓存共享对象,降低内存消耗。

适用场景

享元模式经常与工厂模式一起使用,从而减少相同对象的重复创建。

使用方式

优缺点

优点

缺点