抽象工厂模式

简介:

抽象工厂模式是工厂方法模式的进一步推广。它的定义是:

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。

抽象工厂的使用场景如下:

客户端不关心产品类实例如何被创建、组合和表达的细节。这一条对于所有的工厂模式都是重要的;

这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品,这是抽象工厂的原始定义;

系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

在本文举例中,会出现蒙牛牛奶、蒙牛酸奶,伊利牛奶、伊利酸奶等产品,而蒙牛的牛奶和酸奶就是一个产品族。蒙牛专卖店只消费蒙牛产品族而不会消费伊利的产品族。

抽象工厂的一般组成:

抽象工厂、具体工厂、产品族。

本文会演示抽象工厂的实例以及与其它工厂模式的对比,以及spring中如何应用工厂模式。

实例

业务场景

每一个奶制品专卖店都要进属于自己的产品族,例如蒙牛专卖店消费蒙牛产品族,伊利专卖店消费伊利产品族。

  • 抽象牛奶接口
package com.faith.net;

/**
 * 牛奶抽象接口
 */
public interface Milk {

    /**
     * 获取一个标准产品
     * @return
     */
    public String getName();

}
  • 抽象酸奶接口
package com.faith.net;

/**
 * 抽象酸奶接口
 */
public interface Yoghourt {

    /**
     * 获取一个标准产品
     * @return
     */
    public String getName();

}
  • 蒙牛牛奶类
package com.faith.net;

/**
 * 蒙牛牛奶
 */
public class MengniuMike implements Milk {
    @Override
    public String getName() {
        return "蒙牛牛奶";
    }
}
  • 蒙牛酸奶类
package com.faith.net;

/**
 * 蒙牛酸奶
 */
public class MengniuYoghourt implements Yoghourt {
    @Override
    public String getName() {
        return "蒙牛酸奶";
    }
}
  • 伊利牛奶类
package com.faith.net;

import com.faith.net.Milk;

/**
 * 伊利牛奶
 */
public class YiliMike implements Milk {
    @Override
    public String getName() {
        return "伊利";
    }
}
  • 伊利酸奶类
package com.faith.net;

/**
 * 伊利酸奶
 */
public class YiliYoghourt implements  Yoghourt {
    @Override
    public String getName() {
        return "伊利酸奶";
    }
}
  • 抽象工厂

抽象工厂中包含了具体工厂必须实现的方法定义。

package com.faith.net.abstr;

import com.faith.net.Milk;
import com.faith.net.Yoghourt;

/**
 * 抽象工厂
 */
public abstract class AbstractFactory {

    /**
     * 获得牛奶
     * @return
     */
    public abstract Milk getMike();

    /**
     * 获得酸奶
     * @return
     */
    public abstract Yoghourt getYoghourt();
}
  • 蒙牛工厂
package com.faith.net.abstr;

import com.faith.net.*;

/**
 * 蒙牛工厂
 */
public class MengniuFactory extends  AbstractFactory {

    @Override
    public Milk getMike() {
        return new MengniuMike();
    }

    @Override
    public Yoghourt getYoghourt() {
        return new MengniuYoghourt();
    }
}
  • 伊利工厂
package com.faith.net.abstr;

import com.faith.net.*;

/**
 * 伊利工厂
 */
public class YiliFactory extends  AbstractFactory {

    @Override
    public Milk getMike() {
        return new YiliMike();
    }

    @Override
    public Yoghourt getYoghourt() {
        return new YiliYoghourt();
    }
}
  • 客户端
package com.faith.net.abstr;

/**
 * 客户端
 */
public class AbstractFactoryTest {

    public static void main(String[] args) {
        AbstractFactory factory = new MengniuFactory();

        factory.getMike();
        factory.getYoghourt();
    }
}

当我们需要添加新的产品族时,例如特仑苏的牛奶和酸奶,只需要扩展AbstractFactory和Mike、Yoghourt就好。

抽象工厂与工厂方法

抽象工厂中,包含多个抽象产品类,每个抽象产品可以派生出多个具体产品类;工厂方法中只有一个可以派生出多个具体产品的抽象产品类。

抽象工厂中,每个具体工厂可以创建多个具体产品类的实例;工厂方法中只能创建一个具体产品类的实例。

spring 对抽象工厂的改进

根据抽象工厂的使用场景知道,一个项目只会一种产品族,所以这里的项目只会使用MengniuFactory或YiliFactory其中一种。

从上面的实现可以看出,客户端使用时需要明确指定要使用哪一个factory:

 AbstractFactory factory = new MengniuFactory();

这样造成的问题是,假如代码中有100个地方都使用了MengniuFactory,而业务需求需要全部更改为YiliFactory时,则需要手动改100个地方为:

 AbstractFactory factory = new YiliFactory();

这种方式的体验很不好,可以使用与简单工厂结合的方式实现:

package com.faith.net.abstr;

import com.faith.net.MengniuMike;
import com.faith.net.MengniuYoghourt;
import com.faith.net.Milk;
import com.faith.net.Yoghourt;

/**
 * 获取奶制品工厂对象的工具类
 */
public class MilchigsFactory {

    public static String FACTORY_NAME = "mengniu";

    public static AbstractFactory getMilchigsFactory() {
        AbstractFactory factory = null;

        switch (FACTORY_NAME) {
            case "mengniu":
                factory = new MengniuFactory();
                break;
            case "yili":
                factory = new YiliFactory();
                break;
             default:
                 throw new RuntimeException("wrong milchigsFactory name");
        }

        return factory;
    }
}

添加这个工厂类,客户端使用时就可以直接使用:

MilchigsFactory.getMilchigsFactory();

而我们更改为YiliFactory只需要更改FACTORY_NAME的值就可以了。

但是这样,出现了简单工厂的巨大缺陷,就是当新增TelunsuFactory(特仑苏工厂)时,势必要修改case语句,违反开闭原则。这种情况可以通过配合反射来解决,我们把FACTORY_NAME的值放到专用的配置文件中,然后由程序读取配置文件,获得相应的factory对象。

例如,FACTORY_NAME在配置文件中的值可以类似如下:

FACTORY_NAME = com.faith.net.abstr.MengniuFactory

spring中数据库连接就是通过反射+抽象工厂,例如db连接可能为Mysql、Oracle等,如果使用Mysql,只要如下配置就好了:

driverClass=com.mysql.jdbc.Driver

这样,让程序更优雅、高效。

目录
相关文章
|
1月前
|
设计模式 C#
C# 一分钟浅谈:工厂模式与抽象工厂模式
【10月更文挑战第10天】本文介绍了面向对象编程中的两种常见创建型设计模式:工厂模式和抽象工厂模式。工厂模式通过共同接口创建对象,隐藏创建逻辑,提高封装性和扩展性;抽象工厂模式则提供了一系列相关对象的创建接口,适用于多个相关产品族的创建。文章通过C#代码示例详细解释了这两种模式的实现和应用场景,并讨论了它们的优点、缺点及常见问题。
55 19
|
6月前
|
设计模式
工厂模式与抽象工厂模式
​ 如有错误或有补充,以及任何的改进意见,请在评论区留下您的高见 工厂模式 工厂模式是一种创建型设计模式。在工厂模式中,类的实例化过程通常是通过一个专门的工厂类来完成的,而不是直接在客户端代码中进行的。这样可以提供更好的封装,使得客户端代码与具体产品的实现解耦,增加代码的灵活性和可维护性。 优点 封装性:客户端代码不再需要知道所创建对象的详细信息,只需要与工厂接口进行交互即可。 灵活性:工厂模式使得系统更加灵活,可以在不修改客户端代码的情况下更换或添加新产品。 可扩展性:通过使用工厂模式,当需要添加新产品时,只需要扩展工厂类,而不需要修改客户端代码。 基本结构 抽象产品(Abstra
53 0
|
6月前
|
Java API 数据库
工厂方法模式
工厂方法模式
38 0
|
设计模式
2023-6-11-第二式抽象工厂模式
2023-6-11-第二式抽象工厂模式
75 0
|
设计模式 C++
2023-6-10-第一式工厂方法模式
2023-6-10-第一式工厂方法模式
82 0
|
设计模式 监控 uml
剖析工厂方法模式、从开电脑店开始
工厂类负责把实例化类时,复杂的过程透明化,同时避免大量实例化类的重复代码的出现。
剖析工厂方法模式、从开电脑店开始
|
设计模式
工厂方法模式详细介绍
工厂方法模式(Factory Method Pattern)是一种创建型设计模式,它提供了一种将对象的创建逻辑封装在工厂类中的方式,使得客户端代码无需直接依赖具体的类来创建对象。
103 0
|
XML 存储 JSON
抽象工厂模式
抽象工厂模式