[java设计模式]我眼中的抽象工厂模式

千呼万唤的抽象工厂模式,它来了!

上一篇分享的工厂模式,它在这里


1. 什么是抽象工厂模式?

定义 :抽象工厂模式(Abstract Factory),提供了一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。

假设你已经了解了工厂模式,那么我们对比一下工厂模式和抽象工厂的类图:

工厂模式:

在这里插入图片描述

抽象工厂模式:

在这里插入图片描述

2. 如何理解抽象工厂模式?

在介绍工厂模式的时候时候,我自己胡诌了一个面点和面点厂的例子,这次,我们还是用这个例子进行说明,只是稍微换一个方式:

在这里插入图片描述

说明:

  • 面点厂相当于父类,具体的面点继承于面点厂的“加面”和“加糖”的方法
  • 糖厂和面粉厂属于是具体的实现类的抽象,可以是接口(推荐),也可以是父类

例子仅用于理解,并不一定符合实际,诸君有更好的例子,欢迎借我学习一下!

个人觉得,多多的画画图,思考一下,会加深理解!

3. 代码实现抽象工厂

光说不练,代码出现:

按照上面的类图,我们来个例子:

首先,我们有一个User接口:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:02 下午
 */
public interface User {
    /**
     * 得到用户来自哪里
     */
     void userFrom();
}

和一个Department接口:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:06 下午
 */
public interface Department {

    /**
     * 得到用户的部门
     */
    abstract void myDepartment();
}

我们通过一个工厂接口,返回具体的人和部门:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:02 下午
 */
public interface IFactory {
    abstract User getUser();
    abstract Department getDepartment();

}

我们有一个中国的工厂,继承自IFactory:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:06 下午
 */
public class ChineseFactory implements IFactory {


    @Override
    public User getUser() {
        return new Chinese();
    }

    @Override
    public Department getDepartment() {
        return new China();
    }
}

另外还有一个外国工厂(没错,在我看来,除了中国工厂,都是其他工厂):

 package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:15 下午
 */
public class ForeginFactory implements IFactory {
    @Override
    public User getUser() {
        return new Foreigner();
    }

    @Override
    public Department getDepartment() {
        return new Foreign();
    }
}

作为一名中国人:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:11 下午
 */
public class Chinese implements User {
    @Override
    public void userFrom() {
        System.out.println("i'm a chinese!");
    }
}

我在中国上班:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:13 下午
 */
public class China implements Department {
    @Override
    public void myDepartment() {
        System.out.println("我在中国公司上班!");
    }
}

当然,外国人:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:12 下午
 */
public class Foreigner implements User {
    @Override
    public void userFrom() {
        System.out.println("i'm from England");
    }
}

在外国上班:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:13 下午
 */
public class Foreign implements Department {
    @Override
    public void myDepartment() {
        System.out.println("i'm working at America!");
    }
}

写一个Main,测试一下:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:16 下午
 */
public class Main {
    public static void main(String[] args) {
        IFactory iFactory = new ChineseFactory();

        User user = iFactory.getUser();
        user.userFrom();

        Department department = iFactory.getDepartment();
        department.myDepartment();

        System.out.println("======================");

        IFactory iFactory1 = new ForeginFactory();

        User user1 = iFactory1.getUser();
        user1.userFrom();

        Department department1 = iFactory1.getDepartment();
        department1.myDepartment();
    }
}

输出:

i'm a chinese!
我在中国公司上班!
======================
i'm from England
i'm working at America!

4. 抽象工厂模式的优缺点

就这样,一个简单的抽象工厂模式就结束了!

例子完了,我们说一下它的优缺点:

优点:

  • 易于交换产品系列,由于具体工厂类在只在初始化时出现一次,这就使得改变一个应用的具体工厂变得非常容易,它只需要改变具体工厂即可使用不同的产品配置

即上例中,如果你想让变为外国工厂,只需要改IFactory iFactory = new ChineseFactory();中的具体实现类即可

  • 让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户代码中。

在上例中,我们只看到User和Department,并不知道是中国还是外国。

缺点:

当然了,缺点也是有的,加入此时,我们新增了一个“产品类”,我们就需要增加一个工厂IProduct,以及其子类ChineseProduct和ForeignProduct,而且还要修改IFactory、ChineseFactory和ForeignFactory,是不是很麻烦?

4.1 简单工厂模式解决新增抽象工厂的问题

所以,我们可以使用一下简单工厂模式,来封装一下所有的工厂:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/8/6 3:53 下午
 */
public class FactoryHandler {
    private String country;

    public FactoryHandler(String country) {
        this.country = country;
    }

    public User createUser(){
        switch (country){
            case "Foreign":
                return new Foreigner();
            case "China":
                return new Chinese();
            default:
                return null;
        }
    }

    public Department createDep(){
        switch (country){
            case "Foreign":
                return new Foreign();
            case "China":
                return new China();
            default:
                return null;
        }
    }


}

此时,客户端改成这样:

package com.aran.DesignPatterns.AbstractFactory;

/**
 * @Author Aran
 * @Date 2020/3/19 8:16 下午
 */
public class Main {
    public static void main(String[] args) {
//        IFactory iFactory = new ChineseFactory();
//
//        User user = iFactory.getUser();
//        user.userFrom();
//
//        Department department = iFactory.getDepartment();
//        department.myDepartment();
//
//        System.out.println("======================");
//
//        IFactory iFactory1 = new ForeginFactory();
//
//        User user1 = iFactory1.getUser();
//        user1.userFrom();
//
//        Department department1 = iFactory1.getDepartment();
//        department1.myDepartment();


        User user =  new FactoryHandler("China").createUser();
        Department department = new FactoryHandler("Foreign").createDep();

        user.userFrom();
        department.myDepartment();




    }
}

注意我的传参!

输出结果:

i'm a chinese!
i'm working at America!

4.2 反射解决简单工厂导致的问题

于是,这样就解决了需要增加AbstractFactory实现的问题。

你以为完了?其实我也以为完了!

然而,这样的情况就是,但如果我们除了China和Foreign时,需要增加一个Alien时,麻烦来了,我们要在FactoryHandler 中每一个 create* 方法中为switch增加一个分支!!!


好吧,既然这么多可能新增的情况,我们不知道有多少个抽象工厂,是不是可以考虑一下————————————————反射

此时此刻,我才发现,我们的整个例子不太适合用反射模式,利用部分代码讲一下思路:

  • 上一步中我们传递了一个“country”参数,我们以简单工厂中的createDep() 为例:

原来它是这样子的:

public Department createDep(){
        switch (country){
            case "Foreign":
                return new Foreign();
            case "China":
                return new China();
            default:
                return null;
        }
    }
  • 我们使用反射,就可以去掉switch:
 public  Department createDep() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

        return  (Department)Class.forName(FactoryHandler.class.getPackageName() + ".Foreign").getConstructor().newInstance();
//        switch (country){
//            case "Foreign":
//                return new Foreign();
//            case "China":
//                return new China();
//            default:
//                return null;
//        }
    }
  • FactoryHandler.class.getPackageName() 表示当前包名,你想写死也可以!

再次运行,得到输出:

i'm a chinese!
i'm working at America!

完美~

5. 总结

主要内容简单总结下:

  • 抽象工厂模式与工厂模式的对比
  • 抽象工厂模式的代码实现
  • 抽象工厂模式的优缺点
  • 简单工厂模式解决抽象工厂模式中的缺点
  • 反射解决简单工厂结合抽象工厂方式中存在的缺点

我在最好的时候碰到你,是我的运气。

——《一代宗师》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值