工厂模式 工厂方法模式 抽象工厂模式 简单工厂模式 工厂模式于抽象工厂的区别 设计模式
工厂模式
定义一个用于创建对象的接口,让子类实现具体类的创建。工厂将类的实例化延迟的子类。
- 良好的分装性。对于具体产品,只要知道产品名称即可(类名或字符串),封闭了对产品创建的细节。
- 屏蔽具体产品类。通过抽象产品接口,屏蔽了各个具体产品的实现细节。使用者只关心产品接口就行。比如,java中JDBC的模式,可以很方便的从mysql切换到Oracle。
工厂方法模式,是典型的解耦框架。高层模块只需要知道产品的抽象类,符合迪米特法则。也符合依赖倒置原则,只依赖产品抽象类。也符合里氏替换原则,使用产品子类替换父产品,没有问题。
工厂方法模式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8gvj7Q6e-1623892635636)(imgs/factoryMethodOriginal.PNG)]
上图就是工厂方法通用的类图,看起来还是比较简单的。
现在我们通过一个实际的案例来说明一下。我们模拟一下手机的制造过程。
按照通用类图,我们先创一个抽象的工程类: FactoryPhoneAbstract。
public abstract class FactoryPhoneAbstract {
public abstract <T extends Phone> T manufacturePhone(Class<T> c);
}
抽象工厂的实现类,例如我们创建一个比亚迪工厂FactoryBYD,实现了抽象类的制造手机方法。这里我们通过反射的方式来实现。
当然这里的工厂创建的对象比较交单,真实的场景中,可能会比较复杂,步骤较多。所以更加有必要将对象的创建分装在工程里。
public class FactoryBYD extends FactoryPhoneAbstract {
@Override
public <T extends Phone> T manufacturePhone(Class<T> c) {
Phone phone = null;
try {
phone = (T) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) phone;
}
}
接下来我们创建产品Pone接口。这里我们只创建一个打电话的方法call()。
public interface Phone {
void call();
}
接着创建三个具体的手机类: PhoneVivo、PhoneHuaWei、PhoneXiaoMi。
public class PhoneHuaWei implements Phone {
@Override
public void call() {
System.out.println("华为手机打电话");
}
}
public class PhoneVivo implements Phone {
@Override
public void call() {
System.out.println("Vivo手机打电话");
}
}
public class PhoneXiaoMi implements Phone {
@Override
public void call() {
System.out.println("小米手机打电话");
}
}
下面我们创建一个测试类,测试一下。
public class MethodOriginalMain {
public static void main(String[] args) {
FactoryPhoneAbstract factory = new FactoryBYD();
Phone xiaoMi = factory.manufacturePhone(PhoneXiaoMi.class);
xiaoMi.call();
Phone vivo = factory.manufacturePhone(PhoneVivo.class);
vivo.call();
Phone huaWei = factory.manufacturePhone(PhoneHuaWei.class);
huaWei.call();
}
}
通过测试案例我们可以看到,在具体使用某个名牌手机时,
我们只需要告诉工厂手机品牌即可,不用关系具体的手机时怎么生产出来。
否则,我们就得自己一个去创建不同的手机对象,包括具体细节都得使用者自行处理。
这样耦合性太强,后期维护和扩展都会很麻烦。
我们现在把所有类的关系UML梳理一下,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oqKmtWmk-1623892635639)(imgs/factoryMethodOriginal2.PNG)]
通过类图我们可以看出。通过FactoryBYD工厂实现类,
我们可以把创建三个不同手机对象的具体细节,都延迟到三个手机类的各自实现中去。
对于使用手机对象的用户来说,它只要关心Phone接口的打电话方法即可,
不用关系各品牌手机是怎么实现打电话的功能。
而且很容器在扩展一个新的手机品牌产品。
不需要去修改之前的已发布的代码。
简单工厂模式(静态工厂方法)
工厂方法有些变相的使用方式,例如简单工厂,也叫静态工厂方法。
我们将上面的实现方式稍作变更即可。
第一步,删除FactoryPhoneAbstract抽象类。
第二部,将FactoryBYD修改成如下代码所示。这里我们去掉FactoryBYD的继承类,
将manufacturePhone改成了静态方法
(这也是静态工厂方法名称的由来,当然不改成静态方法也是可以的)。
public class FactoryBYD {
public static <T extends Phone> T manufacturePhone(Class<T> c) {
Phone human = null;
try {
human = (T)Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) human;
}
}
这种改造就相当于是对工厂方法的一种简化改造。我们省去了抽象工作这一部分。
替代单例场景
延迟初始化
抽象工厂模式
我们还是先上一个抽象工厂的类图来看看。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81adwFIC-1623892635648)(imgs/factoryAbstract.PNG)]
看上面的类图是不是感觉和工厂方法的类图区别不大。
现在将上面的案例稍微扩展一下。之前比亚迪的代工厂,只为各个品牌手机生产一种类型的手机。
现在如果每个品牌都生产两个版本的手机,一个是旗舰版,一个是青春版。
而且这两个版本的手机是有共性的,有关联的。下面我们就用抽象工厂实现一下这个场景。
创建FactoryPhoneInterface工厂接口,定义三个分别创建小米、华为、vivo三种手机品牌的方法。
相比较工厂方法,这里直接将工厂抽象成接口。当然工厂方法的抽象工厂也是可抽象成接口的。
这里主要的区别,是对不同品牌的手机各自定义了一个方法来完成对手机的构建。
public interface FactoryPhoneInterface {
Phone manufactureXiaoMiPhone();
Phone manufactureHuaWeiPhone();
Phone manufactureVivoPhone();
}
创建Phone接口,相比于之前的场景。这里增加了一个照相功能。
public interface Phone {
// 大电话
void call();
// 照相
void photograph();
}
这里我们假设各个品牌手机打电话的功能都是一样的,只有照相的功能不同。
那么接下来,就不直接构建真实的手机对象。我们构建一个抽象的品牌手机类。
将各个品牌打电话的通用功能全部实现。代码如下:
public abstract class PhoneHuaWeiAbs implements Phone {
@Override
public void call() {
System.out.println("华为手机打电话");
}
}
public abstract class PhoneXiaoMiAbs implements Phone {
@Override
public void call() {
System.out.println("小米手机打电话");
}
}
public abstract class PhoneVivoAbs implements Phone {
@Override
public void call() {
System.out.println("Vivo手机打电话");
}
}
现在各个品牌的抽象类都已构建,而且call方法都实现了。接着,我们创建真实的手机类。
public class HuaWeiFlagship extends PhoneHuaWeiAbs{
@Override
public void photograph() {
System.out.println("华为旗舰版手机,一亿像素。");
}
}
public class HuaWeiYouth extends PhoneHuaWeiAbs{
@Override
public void photograph() {
System.out.println("华为青春版手机,一百万像素");
}
}
public class VivoFlagship extends PhoneVivoAbs{
@Override
public void photograph() {
System.out.println("Vivo旗舰版,一亿像素");
}
}
public class VivoYouth extends PhoneVivoAbs{
@Override
public void photograph() {
System.out.println("Vivo青春版,一百万像素");
}
}
public class XiaoMiFlagship extends PhoneXiaoMiAbs{
@Override
public void photograph() {
System.out.println("小米旗舰版,一亿像素");
}
}
public class XiaoMiYouth extends PhoneXiaoMiAbs{
@Override
public void photograph() {
System.out.println("小米青春版,一百万像素");
}
}
我们创建了三个品牌的旗舰版、青春版手机,打电话的方法都继承各自品牌没有区别。
只是两个版本的手机像素各有差异。
那么我们怎么用工厂制造这些手机呢。
之前的比亚迪代工厂只需要制造各个品牌的同一种手机,现在不一样了需要两种了。
那么就相当于,在工厂方法中,只有一个车间为各个品牌代工制造手机。
现在,居然有两个类型了。那么我们也就相当于要两个车间,来分别制造。
从代码层面来,就相当于我们需要有两个工厂来生产各个品牌的两种手机。
public class FactoryBYDFlagship implements FactoryPhoneInterface {
@Override
public Phone manufactureXiaoMiPhone() {
return new XiaoMiFlagship();
}
@Override
public Phone manufactureHuaWeiPhone() {
return new HuaWeiFlagship();
}
@Override
public Phone manufactureVivoPhone() {
return new VivoFlagship();
}
}
public class FactoryBYDYouth implements FactoryPhoneInterface {
@Override
public Phone manufactureXiaoMiPhone() {
return new XiaoMiYouth();
}
@Override
public Phone manufactureHuaWeiPhone() {
return new HuaWeiYouth();
}
@Override
public Phone manufactureVivoPhone() {
return new VivoYouth();
}
}
这样我们就拥有了两个工厂,分别生产旗舰版、青春版手机。
大家知道,一般旗舰版是高配价格贵销量相对较少,而青春版相对较多。
那么我们就可以通过Client端来协调,制造两台青春版手机,就制造一台旗舰版手机。
这样是为什么需要使用抽象工厂的原因,就是因为,同一产品族是有关联的。
public class FactoryAbsClient {
public static void main(String[] args) {
FactoryPhoneInterface factoryBYDFlagship = new FactoryBYDFlagship();
FactoryPhoneInterface factoryBYDYouth = new FactoryBYDYouth();
System.out.println("====================旗舰版手机产品线=====================");
Phone huaWeiFlagship = factoryBYDFlagship.manufactureHuaWeiPhone();
huaWeiFlagship.call();
huaWeiFlagship.photograph();
Phone xiaomiFlagship = factoryBYDFlagship.manufactureXiaoMiPhone();
xiaomiFlagship.call();
xiaomiFlagship.photograph();
Phone vivoFlagship = factoryBYDFlagship.manufactureVivoPhone();
vivoFlagship.call();
vivoFlagship.photograph();
System.out.println("=====================青春版手机产品线=================");
Phone huaWeiYouth = factoryBYDYouth.manufactureHuaWeiPhone();
huaWeiYouth.call();
huaWeiYouth.photograph();
Phone xiaomiYouth = factoryBYDYouth.manufactureXiaoMiPhone();
xiaomiYouth.call();
xiaomiYouth.photograph();
Phone vivoYouth = factoryBYDYouth.manufactureVivoPhone();
vivoYouth.call();
vivoYouth.photograph();
}
}
Client端的演示案例就没有按照1:2的量来生产手机了,这里只做测试。
从工厂方法模式和抽象工厂模式的案例,我们可以看出它们的区别:
工厂方法模式针对的是一个产品等级结构;
而抽象工厂模式则是针对的多个相关产品等级结构。
上一篇: typescript学习一一类型
下一篇: 设计模式的应用场景(15)--状态模式