在设计模式中,所谓的“实现一个接口”并“不一定”表示“写一个类,并利用implement关键字实现某个Java接口”。“实现一个接口”泛指“实现某个超类型(可以是类或接口)的某个方法”。
工厂模式的意义是将实例化对象的代码提取出来,放到一个类中统一管理和维护,达到和主项目的依赖关系的解耦。从而提高项目的扩展和维护性。
三种工厂模式(简单工厂模式、工厂方法模式、抽象工厂模式)
【1】简单工厂/静态工厂
简单工厂其实不是一个设计模式,反而比较像是一种编程习惯,使用一个工厂类的静态方法来获取具体的"产品"实例
。
public class PizzaStore{
public orderPizza(){
//使用工厂类的静态方法获取Pizza实例
Pizza a1=SimplePizzaFactory.createPizza();
//...
}
}
//工厂类
public class SimplePizzaFactory{
//工厂类的静态方法
public static Pizza createPizza(){
//...
return new CheesePizza();
}
}
public abstract class Pizza {
abstract void prepare();
//...
}
实例uml图如下(核心思想就是将创建实例的地方交给工厂来做):
简单工厂模式是属于创建型模式,是工厂模式的一种。简单工厂模式是由一个工厂对象决定创建出哪一种产品类的实例。简单工厂模式是工厂模式家族中最简单实用的模式。
简单工厂模式:定义了一个创建对象的类,由这个类来封装实例化对象的行为(代码)
在软件开发中,当我们会用到大量的创建某种、某类或者某批对象时,就会使用到工厂模式。
【2】工厂方法模式
① 工厂方法模式
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个,工厂方法让类把实例化推迟到子类。也就是一个抽象类里面有一个抽象方法,该方法用来生产产品,该抽象方法就是工厂方法
。
工厂方法模式能够封装具体类型的实例化。看下面的类图,抽象的Creator提供了一个创建对象的方法的接口,也称为“工厂方法”
。在抽象的Creator中,任何其他实现的方法,都可能使用到这个工厂方法所制造出来的产品,但只有子类真正实现这个工厂方法并创建产品。
这里需要注意的是,“工厂方法让子类决定要实例化的类是哪一个”
。希望不要理解错误,所谓的“决定”,并不是指模式允许子类本身在运行时做决定,而是指在编写创建者类时,不需要知道实际创建的产品是哪一个。选择了使用哪个子类,自然就决定了实际创建的产品是什么。
② 工厂方法
工厂方法用来处理对象的创建,并将这样的行为封装在子类中。这样客户程序中关于超类的代码就和子类对象创建代码解耦了。
- 工厂方法是抽象的,所以依赖子类来处理对象的创建;
- 工厂方法必须返回一个产品。超类中定义的方法,通常使用到工厂方法的返回值。
- 工厂 方法将客户和实际创建具体产品的代码分隔开来;
- 工厂方法可能需要参数(也可能不需要)来指定所要的产品。
所有工厂模式都用来封装对象的创建。工厂方法模式(Factory Method Pattern)通过让子类决定该创建的对象是什么,来达到将对象创建的过程封装的目的。
工厂方法模式的组成元素
③ 工厂方法与简单工厂
简单工厂把全部的事情,在一个地方都处理完了。然而工厂方法却是创建一个框架,让子类决定如何实现
。简单工厂的做法,可以将对象的创建封装起来,但是简单工厂不具备工厂方法的弹性,因为简单工厂不能变更正在创建的产品。
“工厂带来的好处”
将创建对象的代码几种在一个对象或方法中,可以避免代码中的重复,并且更方便以后的维护。这也意味着客户在实例化对象时,只会依赖于接口,而不是具体类。这可以帮助程序员“针对接口编程,而不针对实现编程”,这让代码更具有弹性,可以应对未来的扩展。
④ 依赖倒置原则
这里还涉及到一个设计原则“依赖倒置原则”
。“要依赖抽象,不要依赖具体类”
。这里更强调“抽象”,这个原则说明了:不能让高层组件依赖底层组件,而且,不管高层或底层组件,“两者”都应该依赖于抽象。
所谓“高层”组件 ,是由其他底层组件定义其行为的类。例如PizzaStore是个高层组件,因为它的行为是由比萨定义的:PizzaStore创建所有不同的比萨对象,准备、烘烤、切片、装盒;而比萨本身属于底层组件。
几个指导方针,帮助编码在OO设计中违反依赖倒置原则:
- 变量不可以持有具体类的引用;
- 不要让类派生自具体类;
- 不要覆盖基类中已实现的方法。
实际中,要完全遵守这些指导方针似乎不太可能,但是应该尽量达到这个原则,而不是随时都要遵循这个原则。
⑤ 工厂方法实例
//接口的应用:工厂方法的设计模式
public class TestFactoryMethod {
public static void main(String[] args) {
IWorkFactory i = new StudentWorkFactory();
i.getWork().doWork();
IWorkFactory i1 = new TeacherWorkFactory();
i1.getWork().doWork();
}
}
//接口定义一个工厂方法用来生产产品
interface IWorkFactory{
Work getWork();
}
//子类实现工厂方法创建产品
class StudentWorkFactory implements IWorkFactory{
public Work getWork() {
return new StudentWork();
}
}
//子类实现工厂方法创建产品
class TeacherWorkFactory implements IWorkFactory{
public Work getWork() {
return new TeacherWork();
}
}
interface Work{
void doWork();
}
class StudentWork implements Work{
public void doWork() {
System.out.println("学生写作业");
}
}
class TeacherWork implements Work{
public void doWork() {
System.out.println("老师批改作业");
}
}
【3】抽象工厂模式
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类
。抽象工厂允许客户使用抽象的接口来创建一组相关的产品
,而不需要知道(或关心)实际产出的具体产品是什么
。这样一来,客户就从具体的产品中被解耦。
抽象工厂模式可以将简单工厂模式和工厂方法模式进行整合。从设计层面看,抽象工厂模式就是对简单工厂模式的改进(或者称为进一步的抽象)。
将工厂抽象成两层,AbsFactory(抽象工厂) 和具体实现的工厂子类。程序员可以根据创建对象类型使用对应的工厂子类。这样将单个的简单工厂类变成了工厂簇,更利于代码的维护和扩展。
UML类图
工厂方法和抽象工厂
抽象工厂的方法经常以工厂方法的方式实现
。抽象工厂的任务是定义一个负责创建一组产品的接口
。这个接口内的每个方法都负责创建一个具体产品,同时我们利用实现抽象工厂的子类来提供这些具体的做法。
抽象工厂与工厂方法都是负责创建对象,不同的是工厂方法是通过继承
实现的,而抽象工厂是通过对象的组合实
现的。这意味着利用工厂方法创建对象,需要扩展一个类并覆盖它的工厂方法。
其实整个工厂方法模式,只不过就是通过子类来创建对象
。用这种做法,客户只需要知道他们所使用的抽象类型就可以了,而由子类来负责决定具体类型。换句话说,工厂方法只负责将客户从具体类型中解耦。
其实整个工厂方法模式,只不过就是通过子类来创建对象。用这种做法,客户只需要知道他们所使用的抽象类型就可以了,而由子类来负责决定具体类型。抽象工厂提供了一个用来创建一个产品家族的抽象类型,这个类型的子类定义了产品被产生的方法。要想使用这个工厂,必须先实例化它,然后将它传入一些针对抽象类型所写的代码中。所以和工厂方法意义,抽象工厂可以把客户从所使用的实际具体产品中解耦。抽象工厂的另一个优点是可以把一群相关的产品集合起来。
- 工厂方法使用的是类,而抽象工厂使用的是对象
- 工厂方法使用继承,而抽象工厂使用对象的组合
- 抽象工厂可以把一群相关的产品集合起来;
- 工厂方法允许类将实例化延迟到子类进行;
- 抽象工厂创建相关的对象家族,而不需要依赖他们的具体类;;
- 所有的工程模式都通过减少应用程序和具体类之间的依赖促进松耦合。
使用场景
当你需要创建产品家族和想让制造的相关产品集合起来时,可以使用抽象工厂。
工厂方法可以把客户代码从需要实例化的具体类中解耦。或者如果你目前还不知道将来需要实例化哪些具体类时,也可以用工厂方法。只要把工厂方法继承成子类并实现工厂方法就可。