场景
现需要定制几个品牌的手机,如iPhone、xiaomi、huawei等,根据其功能不同进行定制,再不断的迭代中手机功能需要不停的升级
实例类图
问题来了
- 系统扩展麻烦。如果用户需要一个既能打游戏又有导航的手机,在图中通过增加了一个新的类PlayGameAndMapiPhone来实现,该类既作为PlayGamePhone的子类,又作为MapPhone的子类;但现在很多面向对象编程语言,如iOS、Java等都不支持多重类继承,因此在这些语言中无法通过继承来实现对来自多个父类的方法的重用。此外,如果还需要扩展一项功能,例如增加一个听音乐的功能,它是iPhone(Xiaomi、HUAWEI)类的子类,现在需要一个同时拥有三项功能(打游戏、导航、听音乐)的手机,必须再增加一个类作为三个类的子类,这同样在iOS等语言中无法实现
- 代码重复。如类图,不只是iPhone支持打游戏功能,xiaomi、huawei都需要,该方法具体实现基本相同,代码重复
- 系统庞大,类的数目非常多。如果增加新的功能系统都需要增加大量的具体类,这将导致系统变得非常庞大
问题改进 根本原因在于复用机制的不合理,根据“合成复用原则”,在实现功能复用时,我们要多用关联,少用继承,因此我们可以换个角度来考虑,将PlayGame()方法抽取出来,封装在一个独立的类中,在这个类中定义一个Component类型的对象,通过调用Component的display()方法来显示最基本的构件,同时再通过setScrollBar()方法对基本构件的功能进行增强
表述 (结构型模式)
动态地给一个对象添加一些额外的职责,就扩展功能来说,装饰模式比生成子类更加灵活
装饰模式类图
组合模式类图
- Component(抽象构件):定义一个对象接口,可以给这些对象动态添加职责
- ConcreteComponent(具体构件):定义一个具体对象,可以给这个对象添加一些职责
- Decorator (抽象装饰类):装饰抽象类,继承Component,从外类来扩展Component类的功能,但是对于Component来说,是无需知道Decorator存在的
- ConcreteDecoratorA 和 ConcreteDecoratorB(具体装饰类):具体的装饰对象,起到给Component添加职责的功能
优点
- 对于扩展一个对象的功能,装饰模式比继承更加灵活性,不会导致类的个数急剧增加
- 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的具体装饰类,从而实现不同的行为
- 可以对一个对象进行多次装饰,通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合,得到功能更为强大的对象
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,原有类库代码无须改变,符合“开闭原则”
缺点
使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,大量小对象的产生势必会占用更多的系统资源,在一定程序上影响程序的性能
使用场景
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责
- 对类的职责的扩展是可选的
注意事项
- 尽量保持装饰类的接口与被装饰类的接口相同,这样,对于客户端而言,无论是装饰之前的对象还是装饰之后的对象都可以一致对待
- 尽量保持具体构件类ConcreteComponent是一个“轻”类,也就是说不要把太多的行为放在具体构件类中,我们可以通过装饰类对其进行扩展
示例
需求V1:现需要定制几个品牌的手机,如iPhone、xiaomi、huawei等,根据其功能不同进行定制,再不断的迭代中手机功能需要不停的升级
构建类
代码语言:javascript复制//抽象构件,可以给这些对象动态添加方法
class Phone {
func supportFunctions() {
}
}
//具体构件类,也可以给这个对象添加方法
class iPhone : Phone {
var name : String
init(name:String) {
self.name = name
}
override func supportFunctions() {
print("(self.name)支持以上功能")
}
}
//具体构件类,也可以给这个对象添加方法
class Xiaomi : Phone {
var name : String
init(name:String) {
self.name = name
}
override func supportFunctions() {
print("(self.name)支持以上功能")
}
}
装饰类
代码语言:javascript复制//抽象装饰类,继承Phone从外类来扩展Phone类的功能,但是对于Phone来说,是无需知道PhoneDecorator的存在的
class PhoneDecorator : Phone {
var phone : Phone
init(phone:Phone) {
self.phone = phone
}
override func supportFunctions() {
self.phone.supportFunctions()
}
}
//具体的装饰对象,起到给Phone类添加技能的方法
class PlayGame: PhoneDecorator {
override func supportFunctions() {
self.playGame()
super.supportFunctions()
}
func playGame() {
print("打游戏")
}
}
//具体的装饰对象,起到给Phone类添加技能的方法
class Map : PhoneDecorator{
override func supportFunctions() {
self.map()
super.supportFunctions()
}
func map() {
print("导航")
}
}
客户端
代码语言:javascript复制let iphone = iPhone.init(name: "apple")
let playGamePhone = PlayGame.init(phone: iphone)
let mapPhone = Map.init(phone: playGamePhone)
mapPhone.supportFunctions()
let xiaomi = Xiaomi.init(name: "xiaomi")
let mapXiaomi = Map.init(phone: xiaomi)
mapXiaomi.supportFunctions()
log:
//导航
//打游戏
//apple支持以上功能
//导航
//xiaomi支持以上功能
需求V2:再原有基础上,支持音乐播放功能
只需创建PlayMusic继承PhoneDecorator,客户端直接调用便可
代码语言:javascript复制class PlayMusic : PhoneDecorator {
override func supportFunctions() {
self.playMusic()
super.supportFunctions()
}
func playMusic() {
print("音乐播放")
}
}
客户端
代码语言:javascript复制let xiaomi = Xiaomi.init(name: "xiaomi")
let mapXiaomi = Map.init(phone: xiaomi)
let playMusic = PlayMusic.init(phone: mapXiaomi)
playMusic.supportFunctions()
log
//音乐播放
//导航
//xiaomi支持以上功能