设计模式之工厂模式
简介
工厂模式称为工厂方法模式,是一种创建型设计模式,其在父类提供一个创建对象的方法,允许子类决定实例化对象的类型。
工厂模式是java中最常见的模式,本意是为了提高代码结构的拓展性,让外部更简单的调用。也是去掉if-else 的一种解决方式,缺点是需要实现的类比较多。
工厂模式分为3种:工厂方法模式、简单/静态工厂模式、抽象工厂模式。
核心:将对象的创建和使用做分离。
未用设计模式实例
需求1
有一个花为电子厂,可以生产鼠标,假设我们通过按钮来点击触发生产。
代码如下:
public void huaweiFactory() {
System.out.println("生产鼠标");
}
调用方
public static void main(String[] args) {
new Controller().huaweiFactory();
}
需求2
随着公司不断扩大,业务也更加广泛,该家电子厂也可以生产键盘。
我们一般会增加一个类型来区分。
代码如下:
public void huaweiFactory(Integer type) {
if (type == 1) {
System.out.println("生产鼠标");
} else if (type == 2) {
System.out.println("生产键盘");
}
}
调用方
public static void main(String[] args) {
new Controller().huaweiFactory(1);
new Controller().huaweiFactory(2);
}
需求3
业务又增加,该家电子厂可以生产耳机、数据线、充电宝。
代码如下:
public void huaweiFactoryNew(Integer type) {
if (type == 1) {
System.out.println("生产鼠标");
} else if (type == 2) {
System.out.println("生产键盘");
} else if (type == 3) {
System.out.println("生产耳机");
} else if (type == 4) {
System.out.println("生产数据线");
} else if (type == 5) {
System.out.println("生产充电宝");
}
}
调用方
public static void main(String[] args) {
new Controller().huaweiFactoryNew(1);
new Controller().huaweiFactoryNew(2);
new Controller().huaweiFactoryNew(3);
new Controller().huaweiFactoryNew(4);
new Controller().huaweiFactoryNew(5);
}
小结
果然没有什么需求是 if-else 解决不了的,如果有,那就多写几个。但是也存在诸多问题:
- 实际代码逻辑肯定不是一行,可能几十行上百行,参数也不会是一个,也可能会根据类型增加不同的参数。当增加新的生产线后整个类会变得冗长,阅读难度增加,因为逻辑的不同也会导致难以维护。
- 该类职责过重,违反了单一职责。
- 当增加新的生产时,需要修改原代码,调用方也需要修改,违反了开闭原则。
这时,我们可以用工厂模式来进行优化。
简单/静态工厂模式
简介
简单/静态工厂模式一般我们叫简单工厂模式,由工厂对象决定创建哪个实例。
对于需求2和需求3我们可以进行改进。
概览图
代码结构
代码
创建一个生产方法,子类可以生产鼠标、键盘等。
public interface ProductionService {
void production();
}
生产鼠标
public class MouseServiceImpl implements ProductionService {
@Override
public void production() {
System.out.println("生产鼠标");
}
}
生产键盘
public class KeyboardServiceImpl implements ProductionService {
@Override
public void production() {
System.out.println("生产键盘");
}
}
创建生产工厂,决定生产什么。
public class ProductionFactory {
public ProductionService product(Integer type) {
if (type == 1) {
return new MouseServiceImpl();
} else if (type == 2) {
return new KeyboardServiceImpl();
}
return null;
}
}
调用方
public class Controller {
public static void main(String[] args) {
new ProductionFactory().product(1).production();
new ProductionFactory().product(2).production();
}
}
新增需求
当需要增加需求3中的耳机时
流程图如下:
我们创建一个生产耳机的子类。
public class HeadsetServiceImpl implements ProductionService {
@Override
public void production() {
System.out.println("生产耳机");
}
}
在生产工厂增加类型3
public class ProductionFactory {
public ProductionService product(Integer type) {
if (type == 1) {
return new MouseServiceImpl();
} else if (type == 2) {
return new KeyboardServiceImpl();
} else if (type == 3) {
return new HeadsetServiceImpl();
}
return null;
}
}
调用方增加类型3即可。
public class Controller {
public static void main(String[] args) {
new ProductionFactory().product(1).production();
new ProductionFactory().product(2).production();
new ProductionFactory().product(3).production();
}
}
小结
- 当增加新的生产线,只需要增加生产子类,修改工厂类,结构清晰。
- 只有一个具体工厂来创建对象,代码量较少。
- 不符合开闭原则,因为需要修改工厂类。
工厂方法模式
简介
在工厂方法模式中,核心的工厂类不再负责所有产品的创建,仅负责具体工厂子类必须实现的接口,将创建的工作交给子类。这样的好处是可以使在不修改具体工厂角色的情况下进行拓展。
工厂方法模式是简单工厂模式的衍生、实现开闭原则可拓展。
工厂方法模式有四要素:
- 抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
- 具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
概览图
代码结构
代码
创建抽象产品类
public interface IProduction {
void production();
}
具体产品1:鼠标
public class MouseService implements IProduction {
@Override
public void production() {
System.out.println("生产鼠标");
}
}
具体产品2:键盘
public class KeyboardService implements IProduction {
@Override
public void production() {
System.out.println("生产键盘");
}
}
创建抽象工厂类
public interface IFactory {
IProduction prod();
}
具体工厂1:鼠标
public class MouseFactory implements IFactory {
@Override
public IProduction prod() {
return new MouseService();
}
}
具体工厂2:键盘
public class KeyboardFactory implements IFactory {
@Override
public IProduction prod() {
return new KeyboardService();
}
}
调用方
public class Controller {
public static void main(String[] args) {
IFactory mouseFactory = new MouseFactory();
mouseFactory.prod().production();
IFactory keyboardFactory = new KeyboardFactory();
keyboardFactory.prod().production();
}
}
新增需求
如果需要新增生产耳机。
则增加具体产品耳机和具体工厂耳机即可。
小结
- 明确了各个类的职责,结构清晰。
- 有新的产品增加,只需要增加具体产品和具体工厂即可。不会影响之前的代码,容易维护。
- 需要编写工厂代码,增加了工作量。类的数量增加,复杂度也会增加。
抽象工厂模式
简介
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只生产一个等级的产品,而抽象工厂模式可生产多个等级的产品。
和工厂方法模式一样,也是由抽象工厂、具体工厂、抽象产品和具体产品等4个要素组成,但是略有不同。
- 抽象工厂(Abstract Factory):提供了创建产品的接口,它包含多个创建产品的方法,可以创建多个不同等级的产品。
- 具体工厂(Concrete Factory):主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
- 抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
- 具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
实例
花为电子厂生产鼠标和键盘,公司发展越来越强大,收购了一家电子厂叫大米电子厂。这个电子厂也生产鼠标和键盘,总公司决定兼容一起生产。
第一个结论说过,没有什么需求是 if-else 解决不了的,如果有,那就多写几个。
代码如下:
public void huaweiAndDami(Integer num, Integer type) {
if (num == 1) {
if (type == 1) {
System.out.println("花为电子厂,生产鼠标");
} else if (type == 2) {
System.out.println("花为电子厂,生产键盘");
}
} else if (num == 2) {
if (type == 1) {
System.out.println("大米电子厂,生产鼠标");
} else if (type == 2) {
System.out.println("大米电子厂,生产键盘");
}
}
}
调用方:
public static void main(String[] args) {
new Controller().huaweiAndDami(1, 1);
new Controller().huaweiAndDami(1, 2);
new Controller().huaweiAndDami(2, 1);
new Controller().huaweiAndDami(2, 2);
}
nice,只要 if-else 写的6,就没有实现不了的需求,实现不了就是 if-else 写的不够多。
针对上面的需求,我们就可以使用抽象工厂模式来优化。
概览图
代码结构
代码
大米相关
创建抽象产品类-大米
public interface IDami {
void production();
}
创建具体产品类-鼠标
public class DMouseService implements IDami {
@Override
public void production() {
System.out.println("大米电子厂,生产鼠标");
}
}
创建具体产品类-键盘
public class DKeyboardService implements IDami {
@Override
public void production() {
System.out.println("大米电子厂,生产键盘");
}
}
创建抽象工厂类
public interface IDFactory {
IDami mouse();
IDami keyboard();
}
创建具体工厂类
public class DFactoryService implements IDFactory {
@Override
public IDami mouse() {
return new DMouseService();
}
@Override
public IDami keyboard() {
return new DKeyboardService();
}
}
花为相关
创建抽象产品类-花为
public interface IHuawei {
void production();
}
创建具体产品类-鼠标
public class HMouseService implements IHuawei {
@Override
public void production() {
System.out.println("花为电子厂,生产鼠标");
}
}
创建具体产品类-键盘
public class HKeyboardService implements IHuawei {
@Override
public void production() {
System.out.println("花为电子厂,生产键盘");
}
}
创建抽象工厂类
public interface IHFactory {
IHuawei mouse();
IHuawei keyboard();
}
创建具体工厂类
public class HFactoryService implements IHFactory {
@Override
public IHuawei mouse() {
return new HMouseService();
}
@Override
public IHuawei keyboard() {
return new HKeyboardService();
}
}
调用方
public class Controller {
public static void main(String[] args) {
IHFactory ihFactory = new HFactoryService();
ihFactory.mouse().production();
ihFactory.keyboard().production();
IDFactory idFactory = new DFactoryService();
idFactory.mouse().production();
idFactory.keyboard().production();
}
}
新增需求
如果或许又开了一家子工厂桔子,也生产鼠标键盘。
则如下图拓展即可。
不会影响之前的业务。
小结
- 当新增一个电子厂时,不需要修改原有代码,满足开闭原则。如增加桔子工厂。
- 当增加一个具体产品时,则所有的工厂类都需要进行修改,不满足开闭原则。如增加生产耳机。
- 当系统中只有一个电子厂时,抽象工厂模式就是工厂方法模式。
总结
- 简单工厂模式:由工厂对象决定创建哪个实例,实现简单,但是需要频繁修改工厂类,可能会引发新的bug,不符合开闭原则。
- 工厂方法模式:是简单工厂模式的拓展,结构清晰,拓展功能就是拓展工厂,不需要修改工厂类,但是增加了工作量。
- 抽象工厂模式:是工厂方法模式的拓展,在特定条件下才符合开闭原则。
|