一、工厂模式概要
在面向对象编程中,创建对象实例最常用的方式就是通过 new 操作符构造一个对象实例,但在某些情况下,new 操作符直接生成对象会存在一些问题。举例来说,对象的创建需要一系列的步骤:可能需要计算或取得对象的初始位置、选择生成哪个子对象实例、或在生成之前必须先生成一些辅助对象。 在这些情况,新对象的建立就是一个 “过程”,而不仅仅是一个操作,就像一部大机器中的一个齿轮传动。
针对上面这种情况,我们如何轻松方便地构造对象实例,而不必关心构造对象示例的细节和复杂过程?解决方案就是使用一个工厂类来创建对象。
概述:在工厂模式中,用factory模式代替使用new创建对象,实现了创建者和调用者的分离,方便维护对象。
实现方式:是面向接口编程
主要遵循:开闭原则,对类可以扩展,但是不允许对类修改。
面向对象设计基本原则:
什么是工厂模式
工厂模式其目的是将创建对象的具体过程屏蔽隔离起来,从而达到更高的灵活性,工厂模式可以分为三类:
- 简单工厂模式(Simple Factory)
- 工厂方法模式(Factory Method)
- 抽象工厂模式(Abstract Factory)
这三种模式从上到下逐步抽象,并且更具一般性。《设计模式》一书中将工厂模式分为两类:工厂方法模式与抽象工厂模式。将简单工厂模式看为工厂方法模式的一种特例,两者归为一类。 我们先从以下案例对工厂模式做个初步的了解:
(1)在没有工厂的时代,如果客户需要一款宝马车,那么就需要客户去创建一款宝马车,然后拿来用。
(2)简单工厂模式:后来出现了工厂,用户不再需要去创建宝马车,由工厂进行创建,想要什么车,直接通过工厂创建就可以了。比如想要320i系列车,工厂就创建这个系列的车。
简单工厂模式的核心是定义一个创建对象的接口,将对象的创建和本身的业务逻辑分离
(3)工厂方法模式:为了满足客户,宝马车种类越来越多,如汽油驱动、电力驱动等等,一个工厂无法创建所有的汽车种类,于是又单独分出来多个具体的工厂,每个具体工厂创建一种车型,即具体工厂类只能创建一个或一种具体产品。但是宝马工厂还是个抽象,你需要指定某个具体的工厂才能生产车出来。
工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。
(4)抽象工厂模式:随着各个品牌相继推出了自己的各个驱动类型的车型,客户的选择也越来越多,此时不仅是宝马,崛起了众如吉利、比亚迪等品牌
抽象工厂模式主要用于创建相关对象的家族。当一个产品族中需要被设计在一起工作时,通过抽象工厂模式,能够保证客户端始终只使用同一个产品族中的对象;并且通过隔离具体类的生成,使得客户端不需要明确指定具体生成类;所有的具体工厂都实现了抽象工厂中定义的公共接口,因此只需要改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。
二、简单工厂---工厂---抽象工厂的演变过程
1、简单工厂模式
应用简单工厂创建单个车型(电车,油车,混能车),用户选择车型即创建对应车型。
为了便于理解,可以看下列UML类图的结构:
在创建车型时,电车,油车,混能车都会作为一个具体的车型出现,且同属于”车“这个抽象类,再搭建一个工厂类来根据用户输入的车型,创建对应的车型:
创建抽象接口Car
public interface Car {
public void createCar();
}
创建具体车型类并实现Car接口
public class Ecar implements Car{
@Override
public void createCar() {
System.out.println("造出电车");
}
}
public class Ocar implements Car{
@Override
public void createCar() {
System.out.println("造出油车");
}
}
public class Mcar implements Car{
@Override
public void createCar() {
System.out.println("造出混能车");
}
}
创建工厂类
public class CarFactory {
public Car createCar(String choice) {
Car car = null;
switch (choice) {
case "电车":
car = new Ecar();
break;
case "油车":
car = new Ocar();
break;
case "混能车":
car = new Mcar();
break;
}
return car;
}
}
测试
public class Test1 {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
Car car = carFactory.createCar("油车");
car.createCar();
}
}
优点:
简单工厂模式提供专门的工厂类用于创建对象,实现了对象创建和使用的职责分离,客户端不需知道所创建的具体产品类的类名以及创建过程,只需知道具体产品类所对应的参数即可,通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
缺点:
增加新的具体产品需要修改原来工厂类的判断逻辑代码。违背开闭原则。
在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展维护,并且工厂类集中了所有产品创建逻辑,一旦不能正常工作,整个系统都要受到影响。
2、工厂方法模式
为了解决简单工厂的违反开闭原则,就出现了工厂方法模式,每一个具体车型类,有一个自己的创建自己对象的工厂,从而避免违反开闭原则。并提高代码结构稳定性。
为了便于理解,可以看下列UML类图的结构:
修改CarFactory类改为接口
public interface CarFactory {
public Car createCar();
}
创建具体型类的工厂
public class EcarFactory implements CarFactory{
@Override
public Car createCar() {
return new Ecar(); //电车
}
}
public class OcarFactory implements CarFactory{
@Override
public Car createCar() {
return new Ocar(); //油车
}
}
public class ,carFactory implements CarFactory{
@Override
public Car createCar() {
return new Mcar(); //混动
}
}
测试
public class Test {
public static void main(String[] args) {
CarFactory carFactory = new EcarFactory();
Car car = carFactory.createCar();
car.createCar();
}
}
优点:
用户只需要知道具体工厂的名称就可得到所要的产品,无须知道产品的具体创建过程。
高层模块只需要知道产品的抽象类,无须关心其他实现类,满足迪米特法则、依赖倒置原则和里氏替换原则。不会修改原有的代码,不会违法开闭原则。
缺点:
创建新产品需要创建相应的工厂类和具体类,代码结构相较于简单工厂模式更加复杂一点。
3、抽象工厂模式
在工厂方法模式中,我们使用一个工厂创建一个产品,一个具体工厂对应一个具体产品,但有时候我们需要一个工厂能够提供多个产品对象,而不是单一的对象,这个时候我们就需要使用抽象工厂模式。
可以看到,上面只是对单个品牌的车企做到工厂创建实体,在多品牌的情况下就需要运用到抽象工厂了:
为了便于理解,可以看下列UML类图的结构:
创建电车,油车,混能车的抽象接口
public interface Ecar {
public void createCar();
}
public interface Mcar {
public void createCar();
}
public interface Ocar{
public void createCar();
}
创建具体品牌车型的具体类(其他车型以此类推)
public class BenzEcat implements Ecar {
@Override
public void createCar() {
System.out.println("奔驰电车");
}
}
public class BmwEcat implements Ecar {
@Override
public void createCar() {
System.out.println("宝马电车");
}
}
public class BydEcat implements Ecar {
public void createCar(){
System.out.println("比亚迪电车");
}
}
创建抽象车型工厂接口
public interface CatFactory {
public Ecat createEcar();
public Ocat createOcar();
public Mcat createMcar();
}
创建具体品牌的工厂实现接口工厂CarFactory(其他品牌以此类推)
public class BmwCarFactory implements CarFactory{
@Override
public Ecat createEcar() {
return new BmwEcar();
}
@Override
public Ocat createOcar() {
return new BmwOcar();
}
@Override
public Mcat createMcar() {
return new BmwMcar();
}
}
测试
public class test {
public static void main(String[] args) {
//抽象工厂测试
CarFactory carFactory = new BmwCarFactory();
Ocar ocar = carFactory.createOcat();
ocar.createCat();
}
}
4、抽象工厂模式---简单抽象工厂模式
为了便于理解,可以看下列UML类图的结构:
将工厂类替换为:
public class SimpleAbstractCatFactory {
public Ecar createEcar(String brand) {
Ecar ecar = null;
switch (brand) {
case "比亚迪":
ecar = new BydEcar();
break;
case "宝马":
ecar = new BmwEcar();
break;
case "奔驰":
ecar = new BenzEcar();
break;
}
return ecar;
}
public Ocar createOcar(String brand) {
Ocar ocar = null;
switch (brand) {
case "比亚迪":
ocar = new BydOcar();
break;
case "宝马":
ocar = new BmwOcar();
break;
case "奔驰":
ocar = new BenzOcar();
break;
}
return ocar;
}
public Mcar createMcar(String brand) {
Mcar mcar = null;
switch (brand) {
case "比亚迪":
mcat = new BydMcar();
break;
case "宝马":
mcat = new BmwMcar();
break;
case "奔驰":
mcat = new BenzMcar();
break;
}
return mcar;
}
}
工厂模式小结
工厂方法模式与抽象工厂模式的区别在于:
(1)工厂方法只有一个抽象产品类和一个抽象工厂类,但可以派生出多个具体产品类和具体工厂类,每个具体工厂类只能创建一个具体产品类的实例。
(2)抽象工厂模式拥有多个抽象产品类(产品族)和一个抽象工厂类,每个抽象产品类可以派生出多个具体产品类;抽象工厂类也可以派生出多个具体工厂类,同时每个具体工厂类可以创建多个具体产品类的实例