对课程第十一章进行一个总结
一、创建型模式:
1.工厂方法模式:
当client不知道/不确定要创建哪个具体类的实例,或者不想在client代码中指 明要具体创建的实例时,用工厂方法。 定义一个用于创建对象的接口,让该接口的子类型来决定实例化哪一个类,从 而使一个类的实例化延迟到其子类。
优点:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
灵活性增强,对于新产品的创建,只需多写一个相应的工厂类。
典型的解耦框架。高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。
缺点:
类的个数容易过多,增加复杂度
增加了系统的抽象性和理解难度
例如
二、结构型模式
1.适配器模式:
将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
优点:
客户端通过适配器可以透明地调用目标接口。
复用了现存的类,程序员不需要修改原有代码而重用现有的适配者类。
解决了类之间接口不兼容的问题
在很多业务场景中符合开闭原则。
缺点:
适配器编写过程需要结合业务场景全面考虑,可能会增加系统的复杂性。
增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
课程ppt举了一个将左上角坐标+宽+高转化为左上角坐标+右下角坐标的例子:
2.装饰器模式:
在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。通过递归的方式对每一个特性构造子类,通过委派机制增加到对象上
优点:
装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态的给一个对象扩展功能,即插即用
通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果
装饰器模式完全遵守开闭原则
缺点:
装饰器模式会增加许多子类,过度使用会增加程序得复杂性。
课程ppt中有一个向冰激凌中添加材料的例子,对Icecream接口先实现一个普通的实现类,然后再用一个抽象接口来实现,其构造函数中需要输入一个Icecream,同样声明一个AddTopping函数,再用几个子类来继承该抽象类,其AddTopping函数为父类的AddTopping函数再加入自己的不同的代码。
三、行为类模式
1.策略模式:
有多种不同的算法来实现同一个任务,但需要client根据需要动态切换算法,而不是写死在代码里。为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例。
优点:
算法可以自由切换。
避免使用多重条件判断。
扩展性良好。
缺点:
策略类会增多。
所有策略类都需要对外暴露。
课程中举例为商品的支付方式有多种实现方式,策略模式就是由客户端选择使用那个方式来实现支付这一接口。
2.模板模式:
共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现。模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
优点:
封装不变部分,扩展可变部分。
提取公共代码,便于维护。
行为由父类控制,子类实现。
缺点:
每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
课程中用一个抽象类CarBuilder来举例,CarBuilder中声明了三个抽象函数,一个具体函数来表示通用逻辑,而其两个子类分别用不同的代码来实现了三个抽象函数。
3.迭代器模式:
客户端希望对放入容器/集合类的一组ADT对象进行遍历访问,而无需关心容器的具体类型,也就是说,不管对象被放进哪里,都应该提供同样的遍历方式。
优点:
它支持以不同的方式遍历一个聚合对象。
迭代器简化了聚合类。
在同一个聚合上可以有多个遍历。
在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。
缺点:
由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
PPT中举例如何写一个迭代器
4.访问者模式:
我们使用了一个访问者类,它改变了元素类的执行算法。通过这种方式,元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式,元素对象已接受访问者对象,这样访问者对象就可以处理元素对象上的操作。
优点:
符合单一职责原则。
优秀的扩展性。
灵活性。
缺点:
具体元素对访问者公布细节,违反了迪米特原则。
具体元素变更比较困难。
违反了依赖倒置原则,依赖了具体类,没有依赖抽象。
PPT中先给出了一个通用的实现方式;
然后举了一个计算书本和水果价格的例子,再Book和Fruit中具体实现accept函数即接受调用一个访问者的函数,之后在ShoppingCartVisitorImpl中实现visit函数通过重载分别计算Book和Fruit的价格。
/* Abstract element interface (visitable) */
public interface ItemElement {
public int accept(ShoppingCartVisitor visitor);
}
/* Concrete element */
public class Book implements ItemElement{
private double price;
...
int accept(ShoppingCartVisitor visitor) {
visitor.visit(this);
} }
public class Fruit implements ItemElement{
private double weight;
...
int accept(ShoppingCartVisitor visitor) {
visitor.visit(this);
} }
/* Abstract visitor interface */
public interface ShoppingCartVisitor {
int visit(Book book);
int visit(Fruit fruit);
}
public class ShoppingCartVisitorImpl implements ShoppingCartVisitor {
public int visit(Book book) {
int cost=0;
if(book.getPrice() > 50){
cost = book.getPrice()-5;
}else
cost = book.getPrice();
System.out.println("Book ISBN::"+book.getIsbnNumber() + " cost ="+cost);
return cost;
}
public int visit(Fruit fruit) {
int cost = fruit.getPricePerKg()*fruit.getWeight();
System.out.println(fruit.getName() + " cost = "+cost);
return cost;
} }
public class ShoppingCartClient {
public static void main(String[] args) {
ItemElement[] items = new ItemElement[]{
new Book(20, "1234"),new Book(100, "5678"),
new Fruit(10, 2, "Banana"), new Fruit(5, 5, "Apple")};
int total = calculatePrice(items);
System.out.println("Total Cost = "+total);
}
private static int calculatePrice(ItemElement[] items) {
ShoppingCartVisitor visitor = new ShoppingCartVisitorImpl();
int sum=0;
for(ItemElement item : items)
sum = sum + item.accept(visitor);
return sum;
} }
参考文献:
1https://www.runoob.com/design-pattern/visitor-pattern.html
2https://www.runoob.com/design-pattern/iterator-pattern.html