软件构造 - 设计模式总结之可复用性

一.Structural patterns 结构型模式

Adapter 适配器模式

概念

将某个类/接口转换为client期望的其他形式,从而使原本因接口不匹配而无法在一起使用的两个类可以一起工作
通过增加一个接口,将已存在的子类封装起来,client面向接口编程,从而隐藏了具体子类。
在这里插入图片描述

使用场景

主要应用场景为扩展应用。修改一个已经投产中的接口,比如系统扩展时,需要使用一个已有或新建立的类,但这个类又不符合系统的接口。

代码示例

//定义一个shape接口,四个参数为两个顶点坐标
interface Shape {
	void display(int x1, int y1, int x2, int y2);
}
//利用适配器调整参数列表不匹配的问题
class Rectangle implements Shape {
	void display(int x1, int y1, int x2, int y2) {
		new LegacyRectangle().display(x1, y1, x2-x1, y2-y1); //adaptor
	}
}
//长方形类包含四个参数为左下顶点坐标和长方形的长和宽
class LegacyRectangle {
	void display(int x1, int y1, int w, int h) {...}
}
//客户端初始化时,任然按照已知的shape类建立,但内部实现经过adaptor调整为rectangle
//将客户端与内部实现隔离
class Client {
	//初始化rectangle
	Shape shape = new Rectangle();
	public display() {
		shape.display(x1, y1, x2, y2);
	}
}

Decorator 装饰器模式

概念

动态地给一个对象添加一些额外的职责。就增加功能来讲,装饰模式比生成子类更加灵活。
在这里插入图片描述

使用场景

  • 需要扩展一个类的功能,或给一个类增加附加功能(如下方代码实例中 UndoStack)
  • 需要动态给一对象增加功能
  • 需要为一批的兄弟类进行改装或加装功能

代码实例

//栈功能定义接口
interface Stack {
	void push(Item e);
	Item pop();
}
//最基础的接口实现类
public class ArrayStack implements Stack {
	... //rep
	public ArrayStack() {...}
	public void push(Item e) {...}
	public Item pop() {...}
}
//对栈进行装饰的类
public class StackDecorator implements Stack {
	protected final Stack stack;
	public StackDecorator(Stack stack) { //在构造时进行委派
		this.stack = stack;
	}
	public void push(Item e) {
		stack.push(e);
	}
	public Item pop() {
		return stack.pop();
	}
	...
}
public class UndoStack
			extends StackDecorator
			implements Stack {//带有回退功能的栈结构
	private final UndoLog log = new UndoLog();
	public UndoStack(Stack stack) { 
		super(stack); //调用父类中的构造方法StackDecorator(stack)
	}
	public void push(Item e) {
		log.append(UndoLog.PUSH, e); //增加了新特性
		super.push(e); //基础功能通过委派实现
	}
	public void undo() {//新功能
		//implement decorator behaviors on stack
	}
	...
}

客户端需要一个具有多种特性的object,通过一层一层的装饰来实现
就像一层层的穿衣服
可以十分灵活的进行功能组织,在运行时才将功能组合,而继承在编译阶段就已经确定

Facade 外观模式

概念

  1. 提供一个统一的接口来取代一系列小接口调用,客户端需要通过一个简化的接口来访问复杂系统内的功能,相当于对复杂系统做了一个封装,简化客户端使用。
  2. 便于客户端学习使用,解耦

使用场景

  • 为一个复杂模块和子系统提供一个供外界访问的接口
  • 子系统相对独立-----外界对子系统的访问只要黑箱操作即可
  • 预防低水平人员带来的风险扩散

代码示例

//两种数据库的具体功能列表 但易用性不够强
public class MySqlHelper {
	public static Connection getMySqlDBConnection() {}
	public void generateMySqlPDFReport (String tableName, Connection con){}
	public void generateMySqlHTMLReport (String tableName, Connection con){}
}
public class OracleHelper {
	public static Connection getOracleDBConnection() {}
	public void generateOraclePDFReport (String tableName, Connection con){}
	public void generateOracleHTMLReport (String tableName, Connection con){}
}
//经过外观模式改造后呈现的方法实现
public class HelperFacade {
public static void generateReport (DBTypes dbType, ReportTypes reportType, String tableName){
	Connection con = null;
	switch (dbType){
	case MYSQL: 
		con = MySqlHelper.getMySqlDBConnection();
		MySqlHelper mySqlHelper = new MySqlHelper();
		switch(reportType){
			case HTML:
				mySqlHelper.generateMySqlHTMLReport(tableName, con);
				break;
			case PDF:
				mySqlHelper.generateMySqlPDFReport(tableName, con);
				break;
		}
		break;
		case ORACLE://与上述相同
	}
public static enum DBTypes { MYSQL,ORACLE; }
public static enum ReportTypes { HTML,PDF;}
}
//客户端调用的区别
//不使用外观模式
String tableName="Employee";
Connection con = MySqlHelper.getMySqlDBConnection();
MySqlHelper mySqlHelper = new MySqlHelper();
mySqlHelper.generateMySqlHTMLReport(tableName, con);
Connection con1 = OracleHelper.getOracleDBConnection();
OracleHelper oracleHelper = new OracleHelper();
oracleHelper.generateOraclePDFReport(tableName, con1);
//使用外观模式
HelperFacade.generateReport(HelperFacade.DBTypes.MYSQL, 
HelperFacade.ReportTypes.HTML, tableName);
HelperFacade.generateReport(HelperFacade.DBTypes.ORACLE, 
HelperFacade.ReportTypes.PDF, tableName);

二.Behavioral patterns 行为类模式

Strategy 策略模式

概念

问题:有多种不同的算法来实现同一个任务,但需要client根据需要动态切换算法,而不是写死在代码里
解决方案:为不同的实现算法构造抽象接口,利用delegation,运行时动态传入client倾向的算法类实例

eg:选择支付方式
在这里插入图片描述

使用场景

  • 多个类只有在算法和行为上稍有不同的场景。
  • 算法需要自由切换的场景
  • 需要屏蔽算法规则的场景
  • 具体策略数量超过4个,需要考虑使用混合模式

代码示例

public interface PaymentStrategy {
	public void pay(int amount);
}
//策略1
public class CreditCardStrategy implements PaymentStrategy {
	private String name;
	private String cardNumber;
	private String cvv;
	private String dateOfExpiry;
	public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate){
		this.name=nm;
		this.cardNumber=ccNum;
		this.cvv=cvv;
		this.dateOfExpiry=expiryDate;
	}
	@Override
	public void pay(int amount) {
	System.out.println(amount +" paid with credit card");
	}
}
//策略2
public class PaypalStrategy implements PaymentStrategy {
	private String emailId;
	private String password;
	public PaypalStrategy(String email, String pwd){
		this.emailId=email;
		this.password=pwd;
	}
	@Override
	public void pay(int amount) {
		System.out.println(amount + " paid using Paypal.");
	}
}
//策略选择
public class ShoppingCart {
...
	public void pay(PaymentStrategy paymentMethod){ //委派机制
		int amount = calculateTotal();
		paymentMethod.pay(amount);
	}
}
//client
public class ShoppingCartTest {
	public static void main(String[] args) {
		ShoppingCart cart = new ShoppingCart();
		Item item1 = new Item("1234",10);
		Item item2 = new Item("5678",40);
		cart.addItem(item1);
		cart.addItem(item2);
		//pay by paypal
		cart.pay(new PaypalStrategy("myemail@exp.com", "mypwd"));
		//pay by credit card
		cart.pay(new CreditCardStrategy(“Alice", "1234", "786", "12/18"));
	}
}

Template 模板模式

概念

问题:做事情的步骤一样,但具体方法不同
解决:共性的步骤在抽象类内公共实现,差异化的步骤在各个子类中实现
使用继承和重写实现模板模式
在这里插入图片描述

使用场景

  • 多个子类有公有方法,并且逻辑基本相同
  • 重要、复杂的算法,可以把核心算法设计为模板方法,周边的相关细节功能则由各个子类实现
  • 【重构时常用】 将相同的代码抽取到父类中,使用钩子函数约束其行为

代码示例

//模板类
public abstract class OrderProcessTemplate {
	public boolean isGift;
	public abstract void doSelect(); //每个类中都有但实现细节不相同,需要分别在每个类中进行定义
	public abstract void doPayment();
	public final void giftWrap() { //共有的方法
		System.out.println("Gift wrap done.");
	}
	public abstract void doDelivery();
	public final void processOrder() {
		doSelect();
		doPayment();
		if (isGift)
			giftWrap();
		doDelivery();
	}
}
//对模板的实现类
public class NetOrder
		extends OrderProcessTemplate {
	@Override
	public void doSelect() {}
	@Override
	public void doPayment() {}
	@Override
	public void doDelivery() {}
}

Interator 迭代器模式

概念

问题:客户端希望遍历被放入容器/集合类的一组ADT对象,无需关心容器的具体类型
也就是说,不管对象被放进哪里,都应该提供同样的遍历方式

代码示例

让自己的集合类实现Iterable接口,并实现自己的独特Iterator迭代器(hasNext, next, remove),允许客户端利用这个迭代器进行显式或隐式的迭代遍历。

  • Iterable接口:实现该接口的集合对象是可迭代遍历的
public interface Iterable<T> {
	...
	Iterator<T> iterator();
}
  • Iterator接口:迭代器
public interface Iterator<E> {
	boolean hasNext();
	E next();
	void remove();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值