工厂设计模式(1)—普通的工厂模式
工厂设计模式(2)—枚举实现工厂模式
序: 为什么需要工厂模式?
学习工厂模式之前,还是思考下这个问题吧,免得学到一身本领之后,却不知道在哪里使用。
小胖(思考模式):小悠同学,为什么需要工厂模式呀?
小悠(怼人模式):你似不似傻?你想买一个冰箱,需要关注冰箱如何造的吗?交给工厂帮你创建就行了。你需要的是冰箱。冰箱你知道吗?!
小胖(可怜无助又弱小模式):咋一听,很有道理的,但是...但是,我们在一个静态方法里面实现造冰箱的过程,也不一定非得要工厂模式呀。
小悠(教导模式):好吧,我根据我理解的说一下吧。(此时,小胖偷偷的拿出本子)
还是我之前说的那点,用户创建对象的目的就是使用对象。将复杂的,重复的逻辑交给工厂。不仅减少了冗余代码,减少出错,关键是与业务代码
解耦
。这其实就是Spring IOC
的原理之一。一般来说,若只是创建一种对象的话,我们没必要使用工厂模式。但是若是创建多个功能相似的对象呢?根据他们的相似之处,将其整合到一个类中,其实就是工厂类。我们在设计工厂类的时候,还要注意“开放-封闭”原则。
小胖(舔狗模式):小悠好棒!那为什么工厂模式可以有简单工厂模式、工厂模式、抽象工厂模式?
小悠(填坑模式):这就是我说的两点,“开放-封闭原则”
与“相似之处”
了。
相似之处:可以分为“产品等级”
和“产品族”
。工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则需要面对多个产品等级结构。
- 产品等级:苹果手机,华为手机,三星手机。都是手机,只是等级不同。
- 产品族:ipone,ipad,Mac,都是苹果一族的商品。
简单工厂模式:核心是具体工厂类,但是扩展的时候,可能要修改工厂类,不符合“开放-封闭”原则。
工厂方法模式:增加了抽象工厂模式这一角色,那么扩展时,可以增加具体的子类工厂。【注意:对象纵向切分,即产品等级】通过具体工厂获取的是一类对象,比如说是手机对象。
抽象工厂模式:与工厂方法的不同的是:【对象的横向切分,即产品族】,关注的点是一个产品族。比如:具体工厂生产的是:电器总类——苹果品牌:ipone,iPad,Mac;
小胖(明悟模式):嗯,工厂模式这么多实现方式,这不是设计者在卖弄什么。比如工厂方法模式,虽然实现了“开放-封闭”原则,但是,当添加“小米品牌”一系列对象时,每一个具体工厂都需要进行修改。是不是也违反了开闭原则?具体选用什么工厂模式,要根据具体的场景来说。
1. 简单工厂模式
创建类模式
静态工厂
共同父类
1.1 简单工厂模式的定义
简单工厂模式属于类的创建型模式,又叫静态工厂模式
,通常专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
1.2 简单工厂模式的角色
- 工厂角色:
简单工厂模式的核心
,它负责实现创建所有实例的内部逻辑,工厂类可以被外界直接调用,创建所需的产品对象。
- 抽象角色
简单工厂模式:所创建的所有对象的父类,他负责描述所有实例的所共有的公共接口。
- 具体产品角色
简单工厂模式:所创建的具体实例对象(共同接口:抽象角色
)。
1.3 简单工厂模式的代码实现
此时,就应该推荐下我的
反射三部曲
了。
首先我先介绍下,虚拟机类加载过程,大体可以分为三个阶段加载、连接、初始化
。JVM那点事-虚拟机类加载机制
-
加载
阶段,会将class文件读取到虚拟机中,并且在内存(一般是堆)生成Class
对象。 - 反射会导致
初始化
阶段的执行。
复习盘点-反射知多少-1(官网+完整代码实现)。在加载
的时候,会生成Class对象,是反射的入口。
获取到Class对象的三种方法:
对象可用的情况下:
object.getClass();
类型可用但是没有对象的情况下:
Object.class();
类的全限定名可用的情况下:
Class.forName(String className);
简单工厂模式——抽象角色
//抽象角色
public interface Fruit {
//获取水果
public void get();
}
简单工厂模式——具体产品角色
//具体产品对象
public class Apple implements Fruit{
@Override
public void get() {
System.out.println("采集苹果");
}
}
//具体产品对象
public class Banana implements Fruit{
@Override
public void get() {
System.out.println("采集香蕉");
}
}
简单工厂模式——工厂角色
//静态工厂
public class FruitFactory {
//静态工厂方法,获取各个对象实例
public static Fruit getFruit(String type) throws IllegalAccessException, InstantiationException {
if (type.equalsIgnoreCase("apple")) {
//通过反射创建对象
return Apple.class.newInstance();
} else if (type.equalsIgnoreCase("banana")) {
return Banana.class.newInstance();
} else {
throw new RuntimeException("type is illegal");
}
}
//静态方法获取对象实例
public static Fruit getFruitByClassName(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class> name = Class.forName(className);
return (Fruit) name.newInstance();
}
}
简单工厂模式——测试方法
public class TestSimpleFactory {
public static void main(String[] args) {
try {
Apple apple = (Apple) FruitFactory.getFruit("apple");
Banana banana = (Banana) FruitFactory.getFruit("banana");
apple.get();
banana.get();
//方法二获取实例对象
Apple apple1 = (Apple) FruitFactory.getFruitByClassName("com.simpleFactory.Apple");
apple1.get();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
测试结果就不说了,一定是获取到对象了。注意,咱们在工厂角色
中通过反射获取到了实例对象。并且,工厂类可以被外界直接调用,创建所需的产品对象。
1.4 简单工厂模式的优缺点
还是科普一波 类设计的原则 之一——“开放-封闭”原则。啥意思呢?对扩展开放,对修改关闭。
优点:
在这个模式中,工厂类是整个模式的关键所在,它包含了必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。
- “便捷化”创建对象。用户可以直接通过工厂类创建所需的实例,而无需了解这些对象是如何创建和组织的,有利于整个软件结构的优化。
缺点:
我们在上面,工厂类的getFruit
方法可以看出,简单工厂的缺点也体现在其工厂类上。
- “高内聚”方面做得不是很好,工厂类集中了所有实例的创建模式。
- “扩展性”不是很好,当系统中具体产品类不断增多的时候,可能会要求工厂类也要做相应的修改。
2. 工厂方法模式
创建型模式
多态工厂模式
不吹不黑的说哦,简单工厂模式扩展性不是很好,违反了
开放-封闭
原则。那我们如何实现对扩展开发,对修改封闭呢?
小胖:采用“接口”,定义一个工厂接口,将具体的工厂
实现推迟到子类当中。工厂接口
不进行修改,通过子类扩展工厂接口
。
正如上面所言:核心工厂类
不再负责具体产品
的创建,这样核心类成为一个抽象工厂角色。仅负责具体工厂子类
必须实现的接口。
这样进一步抽象化的好处是:使得工厂方法模式可以在系统不修改具体工厂角色
的情况下引入新的产品。
2.1 工厂模式的角色
抽象工厂角色
工厂方法模式的核心,任何工厂类都必须实现这个接口。具体工厂角色
具体工厂类是抽象工厂的一个实现,负责实例化产品对象。抽象产品角色
工厂方法模式所创建的具体产品角色
的父类,负责描述所有实例所共有的公共接口。具体产品角色
工厂方法模式所创建的具体实例对象。
2.2 工厂模式的代码实现
- 抽象工厂角色
//抽象工厂角色,仅负责具体子类对象的实现接口
public interface FruitFactory {
public Fruit getFruit();
}
- 具体工厂角色
//创建具体工厂角色,继承于抽象工厂角色
public class AppleFactory implements FruitFactory{
@Override
public Fruit getFruit() {
return new Apple();
}
}
public class BananaFactory implements FruitFactory {
@Override
public Fruit getFruit() {
return new Banana();
}
}
抽象产品角色
//抽象角色
public interface Fruit {
//获取水果
public void get();
}
具体产品角色
//具体产品对象
public class Apple implements Fruit{
@Override
public void get() {
System.out.println("采集苹果");
}
}
//具体产品对象
public class Banana implements Fruit{
@Override
public void get() {
System.out.println("采集香蕉");
}
}
测试类代码
public static void main(String[] args) {
FruitFactory appleFactory = new AppleFactory();
Apple apple = (Apple) appleFactory.getFruit();
apple.get();
}
2.3 工厂模式的和简单工厂模式的比较
- 结构上的不同不是很明显,工厂方法类的核心是抽象工厂类,而简单工厂模式把核心放在了
具体类
上。 - 系统扩展新的产品对象时,仅需要添加一个具体对象以及一个具体工厂对象,原有工厂对象不需要进行修改,也不需要修改客户端,很好的符合了“开放-封闭”原则,而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。
3. 抽象工厂模式
只是划分的角度不同,工厂方法模式好似“纵切”,一类对象归到一个具体工厂;而抽象工厂模式好似“横切”,不同对象中相同属性归到一个具体工厂。
3.1 什么是抽象工厂模式
抽象工厂模式是所有形态的工厂模式中最为抽象和最一般性的。抽象工厂模式可以向客户端提供一个接口,使得客户端不必指定产品的具体类型情况下,能够创建多个产品族的产品对象。
【产品族:比如肯德基、麦当劳,都是一个个产品族,一个具体的工厂类,只创建这一个产品族的对象。】
3.2 抽象工厂模式中角色
抽象工厂角色
抽象工厂模式的核心,包含对多个产品结构的声明,任何工厂类都必须实现这个接口。具体工厂角色
具体工厂角色类是抽闲工厂的一个实现,负责实例化某个产品族中的产品对象。抽象角色
抽象模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。具体产品对象
抽象模式所创建的具体实例对象。
3.3 抽象工厂模式代码实现
抽象工厂角色
public interface FruitFactory {
public Fruit getApple();
public Fruit getBanana();
}
具体工厂角色
水果按南方和北方划分。
public class NorthFruitFactory implements FruitFactory{
@Override
public Fruit getApple() {
return new NorthApple();
}
@Override
public Fruit getBanana() {
return new NorthBanana();
}
}
抽象产品角色
//水果接口
public interface Fruit {
//获取水果
public void get();
}
//苹果种类的抽象方法
public abstract class Apple implements Fruit {
public abstract void get();
}
public abstract class Banana implements Fruit {
public abstract void get();
}
具体产品对象
public class NorthApple extends Apple {
@Override
public void get() {
System.out.println("采集北方苹果");
}
}
public class NorthBanana extends Banana{
@Override
public void get() {
System.out.println("采集北方香蕉");
}
}
测试方法
public static void main(String[] args) {
FruitFactory appleFactory = new NorthFruitFactory();
NorthApple apple = (NorthApple) appleFactory.getApple();
apple.get();
}