简单工厂模式 策略模式【设计模式】【学习总结】

来源:互联网 发布:windows app 编辑:程序博客网 时间:2024/04/30 03:14

感觉不懂设计模式的话是不靠谱的,面试的时候面试官问“了解设计模式吗?”,如果回答说“只知道单例模式和工厂模式”感觉就跟没吃过猪肉只见过猪跑一样。所以决心踏踏实实地看一下。书架上有本《大话设计模式》,以前只是简单的翻了一下,现在拿起来好好读,并且认真做一下笔记。声明,文中部分观点来自于原书作者,此处仅用于摘录备忘和学习交流。

简单工厂模式

一句话概括我理解的简单工厂模式就是:给出一个条件,返回不同的实例。当然,这些实例都继承了同样的类或实现了同样的接口,总之就是有同样的功能(方法)。

简单例子

父类

/** * 水果类 */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); // 得到结果800context = new CalChargeContext("fullcut");float price = context.getPrice(1000); // 得到结果700

我们可以惊喜的发现在调用时,我们仅需要与CalChargeContext一个类打交道即可。而且判断选择具体实现的职责落到了Context类上,减轻了客户端的压力。

总结

使用简单工厂模式+策略模式结合的方法有如下优点:

  1. 降低耦合,客户端只需要调用Context即可(策略模式的优点);
  2. 封装了变化,简化客户端职责,减轻了客户端的压力(简单工厂模式的优点);
  3. 简化单元测试,每个具体算法实现可以单独进行单元测试(策略模式的优点)。

// 个人学习记录,若有错误请指正,大神勿喷
// sfg1991@163.com
// 2015/5/21

0 0