感觉不懂设计模式的话是不靠谱的,面试的时候面试官问“了解设计模式吗?”,如果回答说“只知道单例模式和工厂模式”感觉就跟没吃过猪肉只见过猪跑一样。所以决心踏踏实实地看一下。书架上有本《大话设计模式》,以前只是简单的翻了一下,现在拿起来好好读,并且认真做一下笔记。声明,文中部分观点来自于原书作者,此处仅用于摘录备忘和学习交流。
简单工厂模式
一句话概括我理解的简单工厂模式就是:给出一个条件,返回不同的实例。当然,这些实例都继承了同样的类或实现了同样的接口,总之就是有同样的功能(方法)。
简单例子
父类
/**
* 水果类
*/
public abstract class Fruit {
/**
* 味道
* 输出水果的味道
*/
public abstract void taste();
}
子类
/**
* 橘子类
*/
public class Orange extends Fruit {
/**
* 味道
* 输出橘子的味道
*/
@Override
public void taste() {
System.out.println("橘子是酸的");
}
}
/**
* 香蕉类
*/
public class Banana extends Fruit {
/**
* 味道
* 输出香蕉的味道
*/
@Override
public void taste() {
System.out.println("香蕉没什么味");
}
}
/**
* 西瓜类
*/
public class Watermelon extends Fruit {
/**
* 味道
* 输出西瓜的味道
*/
@Override
public void taste() {
System.out.println("西瓜是甜的");
}
}
工厂类
/**
* 水果工厂类
*/
public class FriutFactory {
private static Fruit fruit;
/**
* 根据输入条件,生成不同的实例并返回
*/
public static Fruit generateFruit(String type) {
switch (type) {
case "apple": fruit = new Apple(); break;
case "orange": fruit = new Orange(); break;
case "watermelon": fruit = new Watermelon(); break;
default : break;
}
return fruit;
}
}
当我们输入不同的条件时将得到不同子类的对象,然后我们将其赋给父类,并且调用父类中的方法时,实际调用的是各子类中重写后的方法。
策略模式
一句话概括我理解的策略模式:封装一系列算法,由一个Context来维护一个策略对象。策略模式下大概分三层,Context+Strategy父类层+Strategy子类层。
简单例子
父类
父类用来定义一系列的算法,为Context提供一系列可供重用的算法和行为。也可以说是接口层。
/**
* 计算费用类
*/
public abstract class CalCharge {
/**
* 计算需要支付的费用
* @param price 原价
*/
public abstract float cal(float price);
}
子类
子类继承父类,是各个算法的具体实现。
/**
* 打折类
*/
public class DiscountCalCharge extends CalCharge {
/**
* 计算打八折后需要支付的费用
* @param price 原价
*/
public abstract float cal(float price) {
return price * 0.8;
}
}
/**
* 满减类
*/
public class FullCutCalCharge extends CalCharge {
/**
* 计算优惠后需要支付的费用
* 满300减100
* @param price 原价
*/
public abstract float cal(float price) {
return price - price / 300 * 100;
}
}
Context类
Context用来维护一个策略类的实例,其具体实例依赖于传递的参数。
/**
* 用来维护一个计算费用类的实例
*/
public class CalChargeContext {
/** 计算费用类实例*/
private CalCharge calCharge = null;
/**
* 构造方法
*/
public CalChargeContext(CalCharge calCharge) {
this.calCharge = calCharge;
}
/**
* 计算应该支付的费用
* @param price 原价
*/
public float getPrice(float price) {
return calCharge.cal(price);
}
}
在策略模式中,客户端调用时需要传入一个CalCharge的实例来实例化一个CalChargeContext对象,然后调用CalChargeContext对象的getPrice方法来获取结果。对于getPrice实际上调用各个CalCharge子类的cal方法这个过程,客户端是不知道也无须知道的。
但是我们可以发现如下问题:
客户端在使用时需要与两个类来交互,且需要根据不同条件实例化不同的CalCharge的对象,选择具体实现的压力落在了客户端上。该如何改变来把这个职责转移给Context呢?答案很简单,与简单工厂模式结合即可。
简单工厂模式+策略模式
我们只需要修改Context类,将其维护Strategy策略对象时修改为一个简单工厂模式即可。
我们使用上面的例子,修改如下:
Context类
Context用来维护一个策略类的实例,其具体实例依赖于传递的参数。
/**
* 用来维护一个计算费用类的实例
*/
public class CalChargeContext {
/** 计算费用类实例*/
private CalCharge calCharge = null;
/**
* 构造方法
*/
public CalChargeContext(String type) {
switch (type) {
case "discount": calCharge = new DiscountCalCharge(); break;
case "fullcut": calCharge = new FullCutCalCharge(); break;
default: break;
}
}
/**
* 计算应该支付的费用
* @param price 原价
*/
public float getPrice(float price) {
return calCharge.cal(price);
}
}
这时候我们在客户端调用的时候需要怎么做呢?
CalChargeContext context = new CalChargeContext("discount");
float price = context.getPrice(1000); // 得到结果800
context = new CalChargeContext("fullcut");
float price = context.getPrice(1000); // 得到结果700
我们可以惊喜的发现在调用时,我们仅需要与CalChargeContext一个类打交道即可。而且判断选择具体实现的职责落到了Context类上,减轻了客户端的压力。
总结
使用简单工厂模式+策略模式结合的方法有如下优点:
- 降低耦合,客户端只需要调用Context即可(策略模式的优点);
- 封装了变化,简化客户端职责,减轻了客户端的压力(简单工厂模式的优点);
- 简化单元测试,每个具体算法实现可以单独进行单元测试(策略模式的优点)。
// 个人学习记录,若有错误请指正,大神勿喷
// sfg1991@163.com
// 2015/5/21