欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

工厂模式 工厂方法模式 抽象工厂模式 简单工厂模式 工厂模式于抽象工厂的区别 设计模式

程序员文章站 2024-01-20 11:56:34
...

工厂模式

定义一个用于创建对象的接口,让子类实现具体类的创建。工厂将类的实例化延迟的子类。

  • 良好的分装性。对于具体产品,只要知道产品名称即可(类名或字符串),封闭了对产品创建的细节。
  • 屏蔽具体产品类。通过抽象产品接口,屏蔽了各个具体产品的实现细节。使用者只关心产品接口就行。比如,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的量来生产手机了,这里只做测试。

从工厂方法模式和抽象工厂模式的案例,我们可以看出它们的区别:

工厂方法模式针对的是一个产品等级结构;
而抽象工厂模式则是针对的多个相关产品等级结构。