Head-First工厂模式

289 阅读7分钟

如何创建一个java 对象? new呗

new 有什么问题?

  • 实例化总是公开的进行;
  • 初始化经常造成“耦合”;

设计原则不是说?“针对接口编程,不针对实现编程吗?”,new 创建对象不是在针对实现编程吗?
是的,一旦加入新的类(变化),就需要重新修改代码,使得代码变得越来越难以维护。

如何处理?
第一步:找出变化的部分。(设计原则一,封装变化)

将根据不同条件创建不同对象的代码封装进入一个类中,该类专职创建实例对象。这个类就叫做工厂。 工厂处理创建对象的细节。

静态工厂?

利用静态方法定义的工厂成为静态工厂。使用静态工厂不需要使用实例对象的方法创建对象。但是,静态方法不能通过继承来改变对象的创建行为。

简单工厂

简单工厂不是一个设计模式,而是一种编程习惯。但由于经常被使用,经常被误认为是一种设计模式。

public class SimplePizzaFactory {
    public Pizza createPizza (String type) {
        Pizza pizza = null;
        if ("cheese".equals(type)) {
            pizza =  new CheesePizza();
        }else if ("greek".equals(type)) {
            pizza =  new GreekPizza();
        }else if ("perpperoni".equals(type)) {
            pizza =  new PepperoniPizza();
        }
        return pizza;
    }
}

工厂方法模式

定义:

定义一个创建对象的接口, 但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

所有工厂模式都用来封装对象的创建。
工厂方法用来处理对象的创建, 并将这样的行为封装在子类中。这样,客户程序中关于超类的代码就和子类对象创建代码解耦了。

  • 创建者(如下图PizzaStore)
    创建者定义了抽象的工厂方法,让子类实现此方法制造产品(如createPizza()方法)
    创建者通常会包含依赖于抽象产品的代码,而这些抽象产品由子类制造,创建者不需要知道在哪制造哪种具体产品
    创建者的子类称为具体创建者(如下图NJPizzaStore)。

  • 产品类 (如下图Pizza)
    具体创建者执行制造产品的方法,返回的类型即为产品类。产品类的子类为具体的产品(如SugarPizza或CheesePizza)。

理解“决定”?

由子类“决定”要实例化的类是哪一个。不是指在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择使用哪个子类,自然就决定了实际创建的产品是什么。

一定要使用参数创建对象吗?

不是的。“参数化工厂方法”可以根据传入的参数创建不同的对象。实际上,工厂经常只产生一种对象,不需要参数化。

工厂方法与简单工厂的差异?

工厂方法的实际创建者与简单工厂很相似, 简单工厂在一个地方把全部的事情都处理完,然而工厂方法确实创建一个框架,让子类决定如何实现。
二者都可以将对象的创建封装起来,但是工厂方法更具有弹性,因为工厂方法通过使用不同的具体创建者可以变更创建的产品。

“工厂”究竟有什么好处??

    1. 封装创建对象的代码,避免代码重复,方便维护
    1. 实例化对象时依赖于接口(抽象类或接口),而不是具体类。
    1. 让代码更具有弹性,应对“变化”

依赖倒置原则

要依赖抽象,不要依赖具体类。
该原则类似于“针对接口编程,不针对实现编程”,但更强调“抽象”。这个原则说明了:不能让高层组件依赖低层组件,而且,不管高层或低层组件,“两者”都应该依赖于抽象。
依赖倒置原则,倒置在哪里?
倒置指的是和一般的OO设计的思考方式完全相反。在上图中,高层类PizzaStore 依赖Pizza这一抽象类, 低层的CheesePizza和SugarPizza也依赖于Pizza这一抽象类。就像一个沙漏的结构,而完全的自上而下的依赖结构则像是一个金字塔结构。

如何在实际工作中避免违反依赖倒置原则??

  • 变量不可以持有具体类的引用 如果使用new,就会持有具体类的引用,采用工厂来避免这样的做法
  • 不要让类派生自具体类 派生具体类,就会依赖具体类,请派生一个抽象(接口或抽象类)
  • 不要覆盖基类中已实现的方法。 如果覆盖基类已实现的方法,那么基类就不是一个真正适合被继承的抽象。基类中实现的方法,应该由所有的类共享。
    实际工作中想要完全遵循以上三个方针很难,而且很多时候完全没必要。

抽象工厂模式

定义: 提供一个接口, 用于创建相关对象的家族或依赖对象的家族,而不需要明确指定具体类。

抽象工厂允许客户使用抽象的接口创建一组相关的产品,而不需要关心实际产出的具体类是什么。这样一来,客户从具体的产品中被解耦。

摘图自:《Head First 设计模式》

PizzaIngredientFactory 为原料工厂接口, NJPizzaIngredientFactory为一个具体的原料工厂,它生产特定的Dough、Souce、Cheese
抽象类PizzaStore(披萨店)定义了 createPizza()方法,子类NJPizzaStore(南京披萨店)实现创建Pizza的方法,在准备过程中prepare(),从指定的原料工厂NJPizzaIngredientFactory 获取原料。
在上述过程中,Dough、Souce、Cheeses是一个产品家族,每个原料工厂生产不同的原料。

发现: 抽象工厂的每个方法经常已工厂方法的方式实现。抽象工厂的任务是定义一个负责创建一组产品的接口。接口中得每个方法都负责创建一个具体产品,具体的产品做法由实现抽象工厂的子类提供。

工厂方法与抽象工厂之间的异同??它们之间有什么关联关系??

工厂方法: 使用类, 采用继承的方法,通过子类创建对象。客户只需知道所使用的抽象类,子类负责决定具体类型。只需要一个方法就行。
抽象工厂: 使用对象,采用组合的方法,定义一个产品家族被产生的方法。使用抽象工厂必须先实例化,再将其传入一些针对抽象类型的业务逻辑中。 抽象工厂在扩展产品时则必须改变接口,接口的所有实现类也就必须改变,是很繁重的工作。

相同点: 都能将对象的创建封装起来,使应用程序解耦,并降低对具体实现的依赖。

OO原则总结

  • 封装变化
  • 针对接口编程,不针对实现编程
  • 多用组合,少用继承
  • 为交互对象之间的松耦合设计而努力
  • 类应该对扩展开发,对修改关闭
  • 依赖抽象,不依赖具体类(依赖倒置原则)

要点总结

  • 所有的工厂都是用来封装对象的创建
  • 简单工厂,虽然不是真正的设计模式,但却是跟具体类之间解耦的简单方法
  • 工厂方法使用继承:把对象的创建交给子类,子类实现工厂方法来创建对象
  • 抽象工厂使用对象组合:对象的创建被实现在工厂接口暴露的方法中。
  • 所有工厂模式都通过减少应用程序与具体类之间的依赖促进松耦合。
  • 工厂方法允许类将实例化延迟到子类中进行
  • 抽象工厂创建相关的对象家族,而不须依赖它们的具体类
  • 依赖倒置原则:避免依赖具体类,尽量依赖抽象。
  • 工厂模式帮助我们针对抽象编程,而不针对具体类编程。