软件构造 面向可复用性和可维护性的设计模式

本文详细介绍了设计模式中的创建型、结构型和行为型模式,包括工厂方法、适配器、装饰器、策略、模板方法、迭代器和访问者模式。每个模式都阐述了其原理、优缺点并给出具体应用场景的例子,如动态选择支付策略、增加对象功能和遍历集合等,帮助读者深入理解设计模式在软件开发中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对课程第十一章进行一个总结

一、创建型模式:

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

3http://c.biancheng.net/view/1348.html

4http://c.biancheng.net/view/1361.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值