同算法与数据结构一样,设计模式同样是作为一个优秀程序员必须熟练掌握与应用的基本功,在阅读源码或者优秀的开源项目的同时,不难发现各种设计模式的灵活运用,叹为观止。
接下来一系列博客,将和大家一起分享这些常用的设计模式。
建造者模式
本篇一起来看看装饰者模式是如何工作的,装饰者模式的概念,直接看起来个人认为是比较难以理解与应用的,不妨通过一个例子,一步步的感受装饰者模式的魅力。
本篇博客书写的时候,刚好就要到六一儿童节了,就以糖果屋为例子进行展开啦。
糖果屋的老板有不同的三种糖果,分别是巧克力糖、牛奶糖以及花生糖,同时有红色的糖纸和蓝色的糖纸,还有丝带与贴纸,不同的物件都有不同的价格,这个时候老板想在每一位小朋友选好之后,马上得出价格,老板心想:本boss也是996撸过码的人,不就是糖果的价格吗,设计一个基类Candy,然后用不同的糖果去继承,在组合出不同的价格就ok了,so easy!
但是在组合的时候,老板发现会有很多种组合,导致子类爆炸,例如红色盒子贴纸巧克力糖、蓝色盒子丝带牛奶糖、红色盒子两根丝带花生糖等等,如果以后要添加新的糖果,会导致组合数越来越多...
为了解决这个问题,老板又搬出了尘封已久的葵花宝典,直到看到了装饰者模式..
老板开始用装饰者模式开始设计了
首先,建立糖果的基类,有一个成员方法返回糖果的名字,一个抽象方法返回糖果的花费.
public abstract class Candy {
protected String name;
protected String getName() {
return name;
}
public abstract double getCost();
}
然后使用具体的糖果去继承Candy
public class ChocolateCandy extends Candy {
public ChocolateCandy() {
name = "ChocolateCandy";
}
@Override
public double getCost() {
return 6.99;
}
}
public class MilkCandy extends Candy {
public MilkCandy() {
name = "Milk Candy";
}
@Override
public double getCost() {
return 5.20;
}
}
public class PeanutCandy extends Candy {
public PeanutCandy() {
name = "Peanut Candy";
}
@Override
public double getCost() {
return 6.66;
}
}
有了具体的糖果之后,老板又开始定义核心组件装饰器
public abstract class CandyDecorate extends Candy{
public abstract String getName();
}
定义好了装饰器,老板开始添加各种装饰组件:红色糖纸 蓝色糖纸 丝带 贴纸...
public class RedBox extends CandyDecorate {
private Candy candy;
public RedBox(Candy candy) {
this.candy = candy;
}
@Override
public String getName() {
return candy.getName() + " with RedBox";
}
@Override
public double getCost() {
return 1.2 + candy.getCost();
}
}
public class BlueBox extends CandyDecorate {
private Candy candy;
public BlueBox(Candy candy) {
this.candy = candy;
}
@Override
public String getName() {
return candy.getName() + " with blue box";
}
@Override
public double getCost() {
return 2.2 + candy.getCost();
}
}
public class Ribbon extends CandyDecorate {
private Candy candy;
public Ribbon(Candy candy) {
this.candy = candy;
}
@Override
public String getName() {
return candy.getName() + " with Ribbon";
}
@Override
public double getCost() {
return 0.5 + candy.getCost();
}
}
public class Sticker extends CandyDecorate {
private Candy candy;
public Sticker(Candy candy) {
this.candy = candy;
}
@Override
public String getName() {
return candy.getName() + " with sticker";
}
@Override
public double getCost() {
return 3.0 + candy.getCost();
}
}
终于大功告成,老板开始了测试
public class CandyHouse {
public static void main(String[] args) {
Candy milkCandy = new MilkCandy();
milkCandy = new BlueBox(milkCandy);
milkCandy = new Ribbon(milkCandy);
System.out.println(milkCandy.getName() + " ¥" + milkCandy.getCost());
Candy chocolateCandy = new ChocolateCandy();
chocolateCandy = new RedBox(chocolateCandy);
chocolateCandy = new Sticker(chocolateCandy);
System.out.println(chocolateCandy.getName() + " ¥" + chocolateCandy.getCost());
}
}
通过装饰者模式,再也不用担心子类爆炸,可以方便快捷的对于基本的组件进行装饰.
下面是kotlin代码
abstract class Candy {
var name = "unknown candy"
fun geName(): String {
return name
}
abstract fun cost(): Double
}
class MilkCandy() : Candy() {
init {
name = "Milk Candy"
}
override fun cost(): Double {
return 3.88
}
}
class PeanutCandy() : Candy() {
init {
name = "Peanut Candy"
}
override fun cost(): Double {
return 5.55
}
}
class ChocolateCandy() : Candy() {
init {
name = "Chocolate Candy"
}
override fun cost(): Double {
return 2.88
}
}
abstract class CandyDecorate : Candy() {
abstract fun getCandyName(): String
}
class BlueBox(var candy: Candy) : CandyDecorate() {
override fun getCandyName(): String {
return candy.name + " with blue box"
}
override fun cost(): Double {
return candy.cost() + 1.88
}
}
class RedBox(var candy: Candy) : CandyDecorate() {
override fun getCandyName(): String {
return candy.name + " with red box"
}
override fun cost(): Double {
return candy.cost() + 1.55
}
}
fun main() {
var chocolateCandy: Candy = ChocolateCandy()
chocolateCandy = BlueBox(chocolateCandy)
println(chocolateCandy.getCandyName() + " ¥" + chocolateCandy.cost())
var milkCandy: Candy = MilkCandy()
milkCandy = RedBox(milkCandy)
println(milkCandy.getCandyName() + " ¥" + milkCandy.cost())
}