工厂方法与抽象工厂模式

面向对象设计六大原则:
开闭原则
依赖倒置
里氏替换原则
单一职责
接口隔离
迪米特法则
结构原则:
面向接口编程,原则上对象之间的关系尽量采用组合的方式而不是继承的方式,集成尽量不重写超类的方法。

工厂方法有三种:简单工厂方法,静态工厂方法,抽象工厂方法。
工厂方法模式为创建对象的模式,为了解决的问题是对象创建的问题。

1.简单工厂方法:
类图
这里写图片描述
代码实现如下:

interface Product {
    void print();
}

class P1 implements Product {

    @Override
    public void print() {
        System.out.println("生产出产品p1");
    }

}

class P2 implements Product {

    @Override
    public void print() {
        System.out.println("生产出产品p2");
    }

}
//简单工厂方法
class SimpleFactory {
    public Product getProduct(String productName) {
        if(productName.equals("p1"))
            return new P1();
        else if(productName.endsWith("p2"))
            return new P2();
        else 
            return null;
    }
}

客户端调用:

// 简单工厂模式
        SimpleFactory simpleFactory = new SimpleFactory();

        simpleFactory.getProduct("p1").print();

        simpleFactory.getProduct("p2").print();

结果:

生产出产品p1
生产出产品p2

分析:简单工厂在产品的种类不多的情况下,可以解决问题,但是增加产品要去修改工厂类
这里我们提出三个问题:
1、字符串这样去选择控制对象生成很不方便,容易出错,而且一个函数的功能太臃肿不利于维护。
2、每次需要工厂都需要实例化,浪费内存。
3、如果增加产品,会修改原来的类,不符合开闭原则。

接下来我们进一步优化这个架构,增加它的健壮性。

第一个问题的解决方案

拆分成不同的方法,直接调用方法,生成不同的产品,不用传入参数

class SimpleFactory2 {
    public Product getP1() {
        return new P1();
    }
    public Product getP2() {
        return new P2();
    }
}

客户端调用:

    // 字符串容易出错,拆分函数改进
        System.out.println("----拆封方法,消除字符串----");

        SimpleFactory2 simpleFactory2 = new SimpleFactory2();

        simpleFactory2.getP1().print();

        simpleFactory2.getP2().print();

运行结果:

----拆封方法,消除字符串----
生产出产品p1
生产出产品p2

这样直接调用方法,不用传入参数,分离了接口方法,解决了问题。
然后我们来解决第二个问题。我们创建对象为了节省内存,应该采用静态方法的模式去解决。不用每次都新生成对象吧。

// 第二个问题的解决方案
/*
 * 太过于浪费内存,可以采用静态方法的模式去生成
 */
class SimpleFactory3 {
    public static P1 p1;
    public static P2 p2;
    public static Product getP1() {
        if(p1 == null) 
            p1 = new P1();
        return p1;
    }
    public static Product getP2() {
        if(p2 == null)
            p2 = new P2();
        return p2;
    }
}

运行结果:

---修改静态方法----
生产出产品p1
生产出产品p2

第三个问题就是如果要增加新的具体的产品p3,那就要修改工厂类了,按现在的情况,必须要在类中添加新的方法。这样:

class SimpleFactory3 {
    public static P1 p1;
    public static P2 p2;
    public static P3 p3;
    public static Product getP1() {
        if(p1 == null) 
            p1 = new P1();
        return p1;
    }
    public static Product getP2() {
        if(p2 == null)
            p2 = new P2();
        return p2;
    }
    public static Product getP3() {
        if(p3 == null)
            p3 = new P3();
        return p3;
}

想象如果具体的产品非常多,难道每次都要去修改这个工厂么。这样明显是不符合开闭原则的。好吧。工厂的目的就是生产出产品,我们应该把稳定不变的部分抽象出来,这里工厂的抽象功能就是创造产品。我们定义一个抽象类。

abstract class AbsctractFactory {
    public abstract Product getProduct();
}

我们具体要生产什么产品,由具体的工厂来实现,例如生产产品P1的工厂修改为:

class P1SimpleFactory extends AbsctractFactory {

    @Override
    public Product getProduct() {
        return new P1();
    }

}

同理生产产品P2修改为:

class P2SimpleFactory extends AbsctractFactory {

    @Override
    public Product getProduct() {
        return new P2();
    }

}

客户端运行如下

// 添加产品,不用修改原来的类了
        System.out.println("--抽象出工厂--");

        AbsctractFactory absctractFactoryP1 = new P1SimpleFactory();

        absctractFactoryP1.getProduct().print();

        AbsctractFactory absctractFactoryP2 = new P2SimpleFactory();

        absctractFactoryP2.getProduct().print();

这个时候我们如果需要产品P3:

// 添加新的具体产品,不需要修改现成的代码,只需定义一个新的产品P3实现原来的产品接口,再添加一个具体工厂即可
class P3 implements Product {

    @Override
    public void print() {
        System.out.println("生产出新的产品p3");
    }

}

增加新产品工厂代码

class P3SimpleFactory extends AbsctractFactory {

    @Override
    public Product getProduct() {
        return new P3();
    }

}

客户端调用:

// 添加新的产品

        AbsctractFactory absctractFactoryP3 = new P3SimpleFactory();

        absctractFactoryP3.getProduct().print();

运行结果:

生产出新的产品p3

我们已经将工厂进行了抽象,现在的结构如下
这里写图片描述
这样的好处就是增加新的产品可以不用再修改原来的工厂了,满足了开闭原则。

————-分割线————-
到此我们完成了单一产品接口的创建,但我们如果是后期需要抽象出更多的产品接口,那我们这个系统就不是那么好了。我们可以重新定义抽象工厂的方法。
我们来举个例子,那就是生产CPU和GPU,GPU是图形显卡的核心,AMD和Intel可以设计CPU,AMD,NVIDIA公司设计GPU。一般制造工作交给代工厂TSMC或则是SAMSUNG,多个产品类别,那我们就在抽象工厂方法里添加产品的方法:
两个产品类别(接口):

// 如果要添加新的,添加新的产品类别
interface CPU {
    void print();
}
interface GPU {
    void print();
}
class AMD implements CPU,GPU {

    @Override
    public void print() {
        System.out.println("我是AMD,设计出了CPU,GPU");
    }

}
class Intel implements CPU {

    @Override
    public void print() {
        System.out.println("我是intel,设计出CPU");
    }

}
class NVIDIA implements GPU {

    @Override
    public void print() {
        System.out.println("我是nVIdia,设计出GPU");
    }

}
// 抽象工厂具有两个函数生产产品
abstract class OEMFactory {
    public abstract CPU produceCPU();
    public abstract GPU produceGPU();
}

具体的工厂类:

/class TSMC extends OEMFactory {

    static {
        System.out.println("TSMC生产...");
    }

    @Override
    public CPU produceCPU() {
        return new AMD();
    }

    @Override
    public GPU produceGPU() {

        return new NVIDIA();
    }

}
class SAMSUNG extends OEMFactory {

    static {
        System.out.println("SAMSUNG生产...");
    }
    // 不得不实现该方法 这里怎么解决呢?
    @Override
    public CPU produceCPU() {
        return null;
    }

    @Override
    public GPU produceGPU() {
        return new AMD();
    }

}

客户端调用:

// 添加新的产品类别
        System.out.println("---添加新的产品类别----");

        OEMFactory omeFactory1 = new TSMC();

        omeFactory1.produceCPU().print();
        omeFactory1.produceGPU().print();

        OEMFactory omeFactory2 = new SAMSUNG();

        // 三星不制造CPU omeFactory2.produceCPU().print();
        omeFactory2.produceGPU().print();

就如上面谈及到的,根据接口隔离原则,我们可以实现多个接口的方式去解决继承多余的方法的问题。而且如果有新的产品线,我们只需要定义新的接口(抽象工厂)就可以了,不用去修改原来的接口。
把抽象类拆分成接口之后的效果:

// 这样我们如果要添加新的产品类别(接口),需要修改抽象工厂类,不符合开闭原则
// 我们需要将抽象工厂拆分成多个部分,设计原则里面有个接口隔离原则,将抽象类进行拆分成接口
interface CPUOEMFactory {
    CPU produceCPU();
}
interface GPUOEMFactory {
    GPU produceGPU();
}
class TSMC2 implements CPUOEMFactory,GPUOEMFactory {

    static {
        System.out.println("TSMC生产...");
    }

    @Override
    public GPU produceGPU() {
        return new NVIDIA();
    }

    @Override
    public CPU produceCPU() {
        return new AMD();
    }

}
class SAMSUNG2 implements GPUOEMFactory {

    static {
        System.out.println("SAMSUNG第二个工厂,生产...");
    }

    @Override
    public GPU produceGPU() {
        return new NVIDIA();
    }

}

客户端调用:

// 拆分了接口
        System.out.println("----拆分了接口---");
        CPUOEMFactory cpuOEMFactory = new TSMC2();

        cpuOEMFactory.produceCPU().print();
        // TSMC同时实现了GPU制造
        GPUOEMFactory gpuOEMFactory = new TSMC2();

        gpuOEMFactory.produceGPU().print();

        GPUOEMFactory gpuOEMFactory2 = new SAMSUNG2();

        gpuOEMFactory2.produceGPU().print();

结果:

----拆分了接口---
TSMC生产...
我是AMD,设计出了CPU,GPU
我是nVIdia,设计出GPU
SAMSUNG第二个工厂,生产...
我是nVIdia,设计出GPU

我们只需实现我们需要的抽象类(接口)即可。这样我们保证了多个产品,多个工厂的方式搭建起一个稳定的架构,多扩展开放,对修改关闭。

这里写图片描述
这里写图片描述

如果新增一个产品线:

// 只需要增加抽象工厂类接口,不需要修改现有代码
interface ARMOEMFactory {
    ARM produce();
}
// 具体制造工厂
class TSMC3 implements ARMOEMFactory {
    static {
        System.out.println("TSMC 第三个工厂,制造了...");
    }
    // TSMC既制造高通,也制造苹果ARM,构造方法自己指定
    ARM arm;
    TSMC3(ARM arm) {
        this.arm = arm;
    }

    @Override
    public ARM produce() {
        return arm;
    }

}
class SAMSUNG3 implements ARMOEMFactory {
    static {
        System.out.println("SAMSUNG第三个工厂,制造了...");
    }

    @Override
    public ARM produce() {
        return new Qualcomm();
    }

}

客户端调用:


        // 增加了新的产品类别(接口)ARM
        System.out.println("-----增了新的产品类别-----");
        ARMOEMFactory armFactory = new TSMC3(new Qualcomm());

        armFactory.produce().printARM();

        ARMOEMFactory armFactory2 = new SAMSUNG3();

        armFactory2.produce().printARM();

结果:

-----增了新的产品类别-----
TSMC 第三个工厂,制造了...
我是高通,设计了820 ARM处理器
SAMSUNG第三个工厂,制造了...
我是高通,设计了820 ARM处理器
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值