GoF之工厂模式
GoF之工厂模式
@
每博一文案
苏东波苏轼《望江南 超然台作》
休对故人思故国,且将薪火试新茶,诗酒趁年华
休对故人思故国:它告诉我们应当忘记过去。
且将薪火试新茶:又告诉我们要活好当下。
诗酒趁年华:更告诉我们既要面对未来,又要及时努力
1. 简单说明“23种设计模式”
设计模式:一种可以被重复利用的解决方案。
GoF(Gang of Four),中文名——四人组。
《Design Patterns: Elements of Reusable Object-Oriented Software》(即《设计模式》一书),1995年由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 合著。这几位作者常被称为"四人组(Gang of Four)"。
该书中描述了23种设计模式。我们平常所说的设计模式就是指这23种设计模式。
不过除了GoF23种设计模式之外,还有其它的设计模式,比如:JavaEE的设计模式(DAO模式、MVC模式等)。
GoF23种设计模式可分为三大类:
一. 创建型(5个):解决对象创建问题。
- 单例模式
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
- 原型模式
二. 结构型 (7个):一些类或对象组合在一起的经典结构
- 代理模式
- 装饰模式
- 适配器模式
- 组合模式
- 享元模式
- 外观模式
- 桥接模式
三. 行为型 (11个):解决类或对象之间的交互问题。
- 策略模式
- 模板方法模式
- 责任链模式
- 观察者模式
- 迭代子模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
- 中介者模式
- 解释器模式
而这里我们该篇的主角“工厂模式” 是解决对象创建问题的,所以工厂模式可以归属于创造型设计模式当中。
这里说明一下,为什么我们学习 Spring框架,突然就跑过来学习,工厂模式呢?
原因是:在Spring框架底层当中,使用了大量的工厂模式,而学习工厂模式,有利于我们更加透彻的学习Spring,更加了解Spring框架的底层原理,从而肆无忌惮的使用Spring。
1.2 介绍工厂模式的三种形态
工厂模式通常有三种形态:
- 第一种:简单工厂模式(Simple Factory):不属于23种设计模式之一。简单工厂模式又叫做:静态工厂方法模式(原因是其中定义的是一个static 静态的方法,进行工厂处理生产的)。简单工厂模式是工厂方法模式的一种特殊实现。
- 第二种:工厂方法模式(Factory Method):是23种设计模式之一。
- 第三种:抽象工厂模式(Abstract Factory):是23种设计模式之一。
1.3 简单工厂模式(静态工厂模式)
简单工厂模式的角色包括三个:
- 抽象产品角色
- 具体产品角色
- 工厂类角色
简单工厂模式的代码如下:
抽象产品角色:
抽象产品角色:武器产品,抽象的,自然就是抽象类了 abstract
作用: 用来定义武器的统一的所具备的功能(这里我们给予上)——攻击(武器都具备攻击的功能)
/**
* 抽象产品角色:武器产品
* 抽象的,自然就是 抽象类了 abstract
*/
public abstract class Weapon {
/**
* 所以的武器都要具备攻击
*/
public abstract void attack();
}
具体产品角色:
这里我们定义两个具体产品角色(有关武器的具体产品:Dagger 匕首,Tank 坦克)
同时具体产品角色都 extends继承了 抽象产品角色Weapon,统一上,武器都具备攻击 attack()
/**
* 具体产品: 匕首
* 同样继承 Weapon 抽象武器产品,保持同性
*/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("Dagger 匕首攻击");
}
}
/**
* 具体产品角色:坦克
* 同时具体产品要继承 抽象武器产品,保持武器的同性
*/
public class Tank extends Weapon {
@Override
public void attack() {
System.out.println("Tank 坦克攻击");
}
}
工厂类角色:
抽象工厂类角色:根据不同的武器类型生产武器
注意: 该获取武器的方法是——静态方法(static),要获取什么产品,就看你传什么参数,传Tank获取坦克,传Dagger获取匕首,
简单工厂模式中是通过定义一个静态方法——获取的,所以又被称为:静态工厂方法模式。
/**
* 抽象工厂类角色:
* 根据不同的武器类型生产武器
*/
public class WeaponFactory {
/*
静态方法,要获取什么产品,就看你传什么参数,传Tank获取坦克,传Dagger获取匕首,
简单工厂模式中有一个静态方法,所以被称为:静态工厂方法模式。
*/
public static Weapon get(String type) {
// 参数不对,返回 null
if (null == type || type.length() == 0) {
return null;
}
// 要匕首产品,给匕首
if ("DAGGER".equals(type)) {
return new Dagger();
} else if ("TANK".equals(type)) { // 要坦克给产品,给坦克
return new Tank();
} else {
throw new RuntimeException("暂时不支持该武器的生产制造");
}
}
}
测试程序(客户端程序):
package com.rainbowsea.test;
import com.rainbowsea.bean.Weapon;
import com.rainbowsea.bean.WeaponFactory;
public class Test {
public static void main(String[] args) {
// 我们要坦克
Weapon tank = WeaponFactory.get("TANK");
tank.attack(); // 坦克攻击
// 我们要匕首,拜托武器工厂给我们制造
Weapon dagger = WeaponFactory.get("DAGGER");
dagger.attack(); // 匕首攻击
}
}
1.3.1 简单工厂模式的优缺点:
简单工厂模式的优点:
客户端程序不需要关心对象的创建细节,需要哪个对象时,只需要向工厂索要即可,初步实现了责任的分离。客户端只负责“消费”,工厂负责“生产”。生产和消费分离。
简单工厂模式的缺点:
- 缺点1:工厂类集中了所有产品的创造逻辑,形成一个无所不知的全能类,有人把它叫做上帝类。显然工厂类非常关键,不能出问题,一旦出问题,整个系统瘫痪。
- 缺点2:不符合OCP开闭原则,在进行系统扩展时,需要修改工厂类。
说明一下:Spring中的BeanFactory就使用了简单工厂模式。关于这一点,我们后面会详细说明
1.4 工厂方法模式
工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点。
工厂方法模式的角色包括:
- 抽象工厂角色
- 具体工厂角色
- 抽象产品角色
- 具体产品角色
简单的说:就是一个产品,会对应着一个工厂。
首先,定义好,抽象产品角色,用于统一产品的功能。这里我们还是以武器 为例子,武器统一都具有攻击的作用。
/**
* 武器的抽象产品,
* 同样是统一武器,武器都具备攻击的功能
*/
public abstract class Weapon {
//
public abstract void attack();
}
定义具体产品角色,这里,我们还是以有关武器的具体产品:Dagger 匕首,Tank 坦克)
同时具体产品角色都 extends继承了 抽象产品角色Weapon,统一上,武器都具备攻击 attack() 。这一点和简单工厂模式一致。
/**
* 具体工厂角色(武器匕首)
*/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("Dagger 匕首攻击");
}
}
/**
* 具体工厂角色(武器坦克)
*/
public class Tank extends Weapon{
@Override
public void attack() {
System.out.println("Tank 坦克攻击");
}
}
抽象工厂角色
这里我们定义一个接口,作为抽象工厂角色,用于约束,生产类的工厂的角色的。都要进行一个生产get()
。需要注意的是:这个方法不是静态的,是实例方法,这是与简单工厂模式的一大区别 。
/**
* 武器工厂的接口
* 统一武器工厂的生产
*/
public interface WeaponFactory {
/**
* 这个方法不是静态的,是实例方法,
* 这里是简单工厂模式的最大的区别
*/
Weapon get();
}
具体工厂角色 :
这里是两个分别为了,生产(Dagger 匕首,Tank 坦克)的具体工厂
-
public class DaggerFactory implements WeaponFactory
-
public class TankFactory implements WeaponFactory
/**
* 具体工厂角色
* 生产匕首的工厂
*/
public class DaggerFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Dagger();
}
}
/**
* 坦克的具体工厂角色:
* 专门生产坦克的工厂
*/
public class TankFactory implements WeaponFactory{
@Override
public Weapon get() {
return new Tank();
}
}
客户端测试
package com.rainbowsea.test;
import com.rainbowsea.bean.DaggerFactory;
import com.rainbowsea.bean.TankFactory;
import com.rainbowsea.bean.Weapon;
import com.rainbowsea.bean.WeaponFactory;
public class Test {
public static void main(String[] args) {
// 我们需要匕首
// 调用匕首的工厂,进行生产
WeaponFactory weaponFactory = new DaggerFactory();
Weapon weapon = weaponFactory.get();
weapon.attack(); // 匕首攻击
// 我们需要坦克
// 调用坦克的工厂,进行生产
WeaponFactory weaponFactory2 = new TankFactory();
Weapon weapon2 = weaponFactory2.get();
weapon2.attack(); // 坦克攻击
}
}
这种简单工厂模式,如果想扩展一个新的产品,只要新增一个产品类,再新增一个该产品对应的工厂即可,例如新增:战斗机
从中,我们可以看出,在进行功能扩展的时候,不需要修改之前的源代码(仅仅是添加了,扩展的类,对象而已,并没有修改),显然工厂方法模式符合OCP原则(修改关闭,扩展打开)。
1.4.1 工厂方法模式的优缺点:
工厂方法模式的优点:
- 一个调用者想创建一个对象,只要知道其名称就可以了。
- 扩展性高,如果想要增加一个产品,只需要扩展该产品的一个工厂类就可以了
- 同时屏幕产品的具体实现(运用多态机理),调用者只关心产品的接口
工厂方法模式的缺点:
每次增加一个新产品时,都需要增加一个具体类和对象实现工厂,使得系统种类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事情。
1.5 抽象工厂模式
抽象工厂模式相对于工厂方法模式来说,就是工厂方法模式是针对一个产品系列的,而抽象工厂模式是针对多个产品系列的,即工厂方法模式是一个产品系列一个工厂类,而抽象工厂模式是多个产品系列一个工厂类。
抽象工厂模式特点:抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类的实例。每一个模式都是针对一定问题的解决方案,工厂方法模式针对的是一个产品等级结构;而抽象工厂模式针对的是多个产品等级结果。
抽象工厂中包含4个角色:
- 抽象工厂角色
- 具体工厂角色
- 抽象产品角色
- 具体产品角色
抽象工厂模式的类图如下:
抽象工厂模式代码如下:
第一部分:武器产品族
package com.powernode.product;
/**
* 武器产品族
* @author 动力节点
* @version 1.0
* @className Weapon
* @since 1.0
**/
public abstract class Weapon {
public abstract void attack();
}
package com.powernode.product;
/**
* 武器产品族中的产品等级1
* @author 动力节点
* @version 1.0
* @className Gun
* @since 1.0
**/
public class Gun extends Weapon{
@Override
public void attack() {
System.out.println("开枪射击!");
}
}
package com.powernode.product;
/**
* 武器产品族中的产品等级2
* @author 动力节点
* @version 1.0
* @className Dagger
* @since 1.0
**/
public class Dagger extends Weapon{
@Override
public void attack() {
System.out.println("砍丫的!");
}
}
第二部分:水果产品族
package com.powernode.product;
/**
* 水果产品族
* @author 动力节点
* @version 1.0
* @className Fruit
* @since 1.0
**/
public abstract class Fruit {
/**
* 所有果实都有一个成熟周期。
*/
public abstract void ripeCycle();
}
package com.powernode.product;
/**
* 水果产品族中的产品等级1
* @author 动力节点
* @version 1.0
* @className Orange
* @since 1.0
**/
public class Orange extends Fruit{
@Override
public void ripeCycle() {
System.out.println("橘子的成熟周期是10个月");
}
}
package com.powernode.product;
/**
* 水果产品族中的产品等级2
* @author 动力节点
* @version 1.0
* @className Apple
* @since 1.0
**/
public class Apple extends Fruit{
@Override
public void ripeCycle() {
System.out.println("苹果的成熟周期是8个月");
}
}
第三部分:抽象工厂类
package com.powernode.factory;
import com.powernode.product.Fruit;
import com.powernode.product.Weapon;
/**
* 抽象工厂
* @author 动力节点
* @version 1.0
* @className AbstractFactory
* @since 1.0
**/
public abstract class AbstractFactory {
public abstract Weapon getWeapon(String type);
public abstract Fruit getFruit(String type);
}
第四部分:具体工厂类
package com.powernode.factory;
import com.powernode.product.Dagger;
import com.powernode.product.Fruit;
import com.powernode.product.Gun;
import com.powernode.product.Weapon;
/**
* 武器族工厂
* @author 动力节点
* @version 1.0
* @className WeaponFactory
* @since 1.0
**/
public class WeaponFactory extends AbstractFactory{
public Weapon getWeapon(String type){
if (type == null || type.trim().length() == 0) {
return null;
}
if ("Gun".equals(type)) {
return new Gun();
} else if ("Dagger".equals(type)) {
return new Dagger();
} else {
throw new RuntimeException("无法生产该武器");
}
}
@Override
public Fruit getFruit(String type) {
return null;
}
}
package com.powernode.factory;
import com.powernode.product.*;
/**
* 水果族工厂
* @author 动力节点
* @version 1.0
* @className FruitFactory
* @since 1.0
**/
public class FruitFactory extends AbstractFactory{
@Override
public Weapon getWeapon(String type) {
return null;
}
public Fruit getFruit(String type){
if (type == null || type.trim().length() == 0) {
return null;
}
if ("Orange".equals(type)) {
return new Orange();
} else if ("Apple".equals(type)) {
return new Apple();
} else {
throw new RuntimeException("我家果园不产这种水果");
}
}
}
第五部分:客户端程序
package com.powernode.client;
import com.powernode.factory.AbstractFactory;
import com.powernode.factory.FruitFactory;
import com.powernode.factory.WeaponFactory;
import com.powernode.product.Fruit;
import com.powernode.product.Weapon;
/**
* @author 动力节点
* @version 1.0
* @className Client
* @since 1.0
**/
public class Client {
public static void main(String[] args) {
// 客户端调用方法时只面向AbstractFactory调用方法。
AbstractFactory factory = new WeaponFactory(); // 注意:这里的new WeaponFactory()可以采用 简单工厂模式 进行隐藏。
Weapon gun = factory.getWeapon("Gun");
Weapon dagger = factory.getWeapon("Dagger");
gun.attack();
dagger.attack();
AbstractFactory factory1 = new FruitFactory(); // 注意:这里的new FruitFactory()可以采用 简单工厂模式 进行隐藏。
Fruit orange = factory1.getFruit("Orange");
Fruit apple = factory1.getFruit("Apple");
orange.ripeCycle();
apple.ripeCycle();
}
}
执行结果:
1.6 抽象工厂模式的优缺点:
- 优点: 当一个产品族中的多个对象被设计成一起工作时,它能保证客户端始终只使用同一个产品族中的对象。
- 缺点: 产品族扩展非常困难,要增加一个系列的某一产品,既要在AbstractFactory里加代码,又要在具体的里面加代码。
2. 总结:
23种设计模式:
- 创建型(5个):解决对象创建问题。
- 结构型 (7个):一些类或对象组合在一起的经典结构
- 行为型 (11个):解决类或对象之间的交互问题。
简单工厂模式(静态工厂模式),需要注意是:一个静态方法,所以被称之为”静态工厂模式“,不属于23种设计模式。同时需要注意:简单工厂的一个上帝类和(违背了OCP原则(修改关闭,苦扩展打开)) 的问题。注意其中的优点和缺点,灵活使用。
工厂方法模式既保留了简单工厂模式的优点,同时又解决了简单工厂模式的缺点(类爆炸问题)。工厂方法模式(不是静态方法了,是实例方法)。同时其中的优点和缺点,灵活使用。
抽象工厂模式(解决了工厂方法模式的,类爆炸问题)抽象工厂模式相对于工厂方法模式来说,就是工厂方法模式是针对一个产品系列的,而抽象工厂模式是针对多个产品系列的,即工厂方法模式是一个产品系列一个工厂类,而抽象工厂模式是多个产品系列一个工厂类。其中的优点和缺点,灵活使用。
灵活使用工厂模式的三种形态,处理现实当中的业务需求。
3. 最后:
“在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。”