设计模式---工厂模式

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式---工厂模式相关的知识,希望对你有一定的参考价值。


简单工厂模式

介绍

简单工厂模式属于类的创新型模式,又叫静态工厂方法模式,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。


角色

Factory(工厂角色):工厂角色即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑;工厂类可以被外界直接调用,创建所需的产品对象;在工厂类中提供了静态的工厂方法factoryMethod(),它的返回类型为抽象产品类型Product

Product(抽象产品角色):它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法,它的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。

ConcreteProduct(具体产品角色):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每一个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法

在简单工厂模式中,客户端通过工厂类来创建一个产品类的实例,而无须直接使用new关键字来创建对象,它是工厂模式家族中最简单的一员


简单工厂模式深入分析:

简单工厂模式解决的问题是如何去实例化一个合适的对象。

简单工厂模式的核心思想就是:有一个专门的类来负责创建实例的过程。

具体来说,把产品看着是一系列的类的集合,这些类是由某个抽象类或者接口派生出来的一个对象树。而工厂类用来产生一个合适的对象来满足客户的要求。

如果简单工厂模式所涉及到的具体产品之间没有共同的逻辑,那么我们就可以使用接口来扮演抽象产品的角色;如果具体产品之间有功能的逻辑或,我们就必须把这些共同的东西提取出来,放在一个抽象类中,然后让具体产品继承抽象类。为实现更好复用的目的,共同的东西总是应该抽象出来的。


代码演示

产品的抽象接口:

public abstract class food
{
    //抽象类中存放的公共方法
    public abstract void getFood();
}

建立具体的产品:

public class chicken extends food 
{
    @Override
    public void getFood() {
        System.out.println("鸡排");
    }
}


public class fish extends food{
    @Override
    public void getFood() {
        System.out.println("鱼排");
    }
}

现在建立一个食物加工工厂:

public class FoodFactory
{
    //将获取产品的方法设置为静态方法,那么就成了静态工厂模式
    //方式1:
    //传入一个字符串,判断是什么类型,然后返回相应的实例对象
    public static food getFoodByString(String type) throws InstantiationException, IllegalAccessException {
      if(type.equalsIgnoreCase("chicken"))
      {
          return chicken.class.newInstance();
      }
      else if(type.equalsIgnoreCase("fish"))
      {
          return fish.class.newInstance();
      }
      else
      {
          System.out.println("无法找到对应实例对象");
          return null;
      }
    }

    //方式2:
    //传入一个字节码文件对象,通过反射来创建对象
    public static food getFoodByFanshe(Class c)
    {
        food f=null;
        try {
            f= (food) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return f;
    }
}

最后我们建立测试客户端:

      //传入字符串
        food chicken = FoodFactory.getFoodByString("chicken");
        chicken.getFood();
        //传入字节码文件对象
        food fish = FoodFactory.getFoodByFanshe(fish.class);
        fish.getFood();


优点

  • 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的职责,而仅仅“消费”产品,简单工厂模式实现了对象创建和使用的分离。
  • 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
  • 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点

  • 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
  • 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
  • 系统扩展困难,一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护,且违背开闭原则。
  • 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。

适用场景

  • 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
  • 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

参考文章

设计模式 | 简单工厂模式及典型应用

简单工厂模式


工厂方法模式

介绍

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。

工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。

工厂方法模式是一种类创建型模式。


角色

  • roduct(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类
  • ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
  • Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
  • ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。

与简单工厂模式相比,工厂方法模式最重要的区别是引入了抽象工厂角色,抽象工厂可以是接口,也可以是抽象类或者具体类


工厂方法模式深入分析

  • 工厂方法模式是简单工厂模式的进一步抽象和推广
  • 工厂方法模式保持了简单工厂模式的优点,并克服了它的缺点
  • 核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给其子类去完成
  • 可以允许系统在不修改工厂角色的情况下引进新产品
  • 增加具体产品–>增加具体工厂,符合“开闭原则”

代码演示

<!--引入注解工具类,扫描某个包下的注解-->
        <dependency>
            <groupId>org.reflections</groupId>
            <artifactId>reflections</artifactId>
            <version>0.9.11</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>21.0</version>
        </dependency>

抽象产品:

public abstract class food 
{
    public abstract String getFood();
}

具体产品:

//自定义注解
@Food("chicken")
public class chicken extends food 
{
    @Override
    public String getFood() {
        return "肯德基";
    }
}


@Food("fish")
public class chicken extends food
{
    @Override
    public String getFood() {
        return "肯德基";
    }
}

food注解:

//当前被描述的注解,会被保留到字节码文件中,并被JVM读取到
@Retention(RetentionPolicy.RUNTIME)
//value可以省略,只能作用在类上
@Target(ElementType.TYPE)
public @interface Food
{
    //指定Bean在Map中的一个名字
    //抽象方法
    String value();
}

扫描food注解,放入一个Bean集合

//扫描com包下面标注了food注解的类,放入一个map集合中
public class BeanUtils
{
 private Map<String,Object> InstanceObjects=new HashMap<>();
 BeanUtils()
 {
     //扫描注解,放入Map集合中
     Reflections f=new Reflections("com");
     //获得所有标注了food注解的类
     Set<Class<?>> classes = f.getTypesAnnotatedWith(Food.class);
     //循环,生成一个实例,放入map集合中
     classes.forEach(bean->
     {
         Object beanInstance=null;
         try {
             beanInstance=bean.newInstance();
             //获得类上面标注的注解接口的实现对象
             Food foodAnno = bean.getAnnotation(Food.class);
             String beanName = foodAnno.value();
             //放入Map集合中
             InstanceObjects.put(beanName,beanInstance);
         } catch (InstantiationException e) {
             e.printStackTrace();
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         }
     });
 }
 //获得指定Bean
    public Object getBean(String beanName)
    {
        return InstanceObjects.get(beanName);
    }
}

抽象工厂:

public abstract class factory 
{
    public abstract food getFood();
}

具体工厂:

public class fishFactory extends factory
{
    BeanUtils beanUtils=new BeanUtils();
    @Override
    public food getFood()
    {
        fish f = (fish) beanUtils.getBean("fish");
          return f;
    }
}

public class chickenFactory extends factory
{
    BeanUtils beanUtils=new BeanUtils();
    @Override
    public food getFood()
    {
        chicken f = (chicken) beanUtils.getBean("chicken");
          return f;
    }
}

测试:

public class test
{
    @Test
    public  void test()
    {
        //获得鱼排
        factory f=new fishFactory();
        String food = f.getFood().getFood();
        System.out.println(food);
        //获得鸡排
        f=new chickenFactory();
        String food2 = f.getFood().getFood();
        System.out.println(food2);
    }
}


工厂方法模式总结

工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是很多开源框架和API类库的核心模式。


工厂方法模式的主要优点

  1. 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
  2. 基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够让工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,就正是因为所有的具体工厂类都具有同一抽象父类。
  3. 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合”开闭原则”。

工厂方法模式的主要缺点

  1. 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
  2. 由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,且在实现时可能需要用到DOM、反射等技术,增加了系统的实现难度。

适用场景

  1. 客户端不知道它所需要的对象的类。在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建,可将具体工厂类的类名存储在配置文件或数据库中。
  2. 抽象工厂类通过其子类来指定创建哪个对象。在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。

参考文章

设计模式 | 工厂方法模式及典型应用

Dom读取配置文件和工厂模式结合


抽象工厂模式

介绍

抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,它是一种对象创建型模式。

在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的产品。


前置知识: 产品族,产品等级

产品族:一个品牌下面的所有产品;例如华为下面的手机,路由器,电脑 称为华为的产品族;

产品等级:多个品牌下面的同种产品;例如华为和小米下面的手机称为一个产品等级;


角色

  • AbstractFactory(抽象工厂):它声明了一组用于创建一族产品的方法,每一个方法对应一种产品。
  • ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建产品的方法,生成一组具体产品,这些产品构成了一个产品族,每一个产品都位于某个产品等级结构中。
  • AbstractProduct(抽象产品):它为每种产品声明接口,在抽象产品中声明了产品所具有的业务方法
  • ConcreteProduct(具体产品):它定义具体工厂生产的具体产品对象,实现抽象产品接口中声明的业务方法。

在抽象工厂中声明了多个工厂方法,用于创建不同类型的产品,抽象工厂可以是接口,也可以是抽象类或者具体类

具体工厂实现了抽象工厂,每一个具体的工厂方法可以返回一个特定的产品对象,而同一个具体工厂所创建的产品对象构成了一个产品族


例子

以下图为例,有手机和路由器两种产品,有华为和小米两种品牌,两种品牌都可以生产手机和路由器;

  1. 有手机和路由器两种产品,定义两个接口;
  2. 小米和华为都可以生产这两种产品,所以有4个实现类;
  3. 现在需要创建华为和小米的工厂类,先将工厂类进行抽象,里面有创建两个产品的方法,返回的是产品的接口类;
  4. 创建华为和小米的工厂实现类,继承工厂类接口,实现创建各自产品的方法;
  5. 客户端调用时,直接用工厂接口类创建需要的工厂,拿到对应的产品;


1.手机产品接口和路由器产品接口-----抽象产品层

//手机产品接口
public interface IPhoneProduct {
    //开机
    void start();
    //关机
    void shutdown();
    //打电话
    void callup();
    //发邮件
    void sendSMS();
}

//路由器产品接口
public interface IRouterProduct {
    //开机
    void start();
    //关机
    void shutdown();
    //打开wifi
    void openwifi();
    //设置
    void setting();
}

2.华为和小米的产品的4个实现类----具体产品的实现类

//华为手机实现类
public class HuaweiPhone implements IPhoneProduct {

    @Override
    public void start() {
	System.out.println("开启华为手机");
    }

    @Override
    public void shutdown() {
	System.out.println("关闭华为手机");
    }

    @Override
    public void callup() {
	System.out.println("华为手机打电话");
    }

    @Override
    public void sendSMS() {
	System.out.println("华为手机发邮件");
    }
}

//华为路由器实现类
public class HuaweiRouter implements IRouterProduct {

    @Override
    public void start() {
        System.out.println("开启华为路由器");
    }

    @Override
    public void shutdown() {
	System.out.println("关闭华为路由器");
    }

    @Override
        public void openwifi() {
	System.out.println("打开华为wifi");
    }

    @Override
    public void setting() {
	System.out.println("设置华为路由器");
    }
}

//小米手机实现类
public class XiaomiPhone implements IPhoneProduct {

    @Override
    public void start() {
	System.out.println("开启小米手机");
    }

    @Override
    public void shutdown() {
	System.out.println("关闭小米手机");
    }

    @Override
    public void callup() {
	System.out.println("小米手机打电话");
    }

    @Override
    public void sendSMS() {
	System.out.println("小米手机发邮件");
    }
}

//小米路由器实现类
public class XiaomiRouter implements IRouterProduct {

    @Override
    public void start() {
	System.out.println("开启小米路由器");
    }

    @Override
    public void shutdown() {
	System.out.println("关闭小米路由器");
    }

    @Override
    public void openwifi() {
	System.out.println("打开小米wifi");
    }

    @Override
    public void setting() {
	System.out.println("设置小米路由器");
    }
}

3.工厂接口类----抽象工厂

以上是关于设计模式---工厂模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式学习——简单工厂模式工厂模式抽象工厂模式

设计模式简单工厂模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

设计模式学习——简单工厂模式工厂模式抽象工厂模式

设计模式工厂方法模式 ( 简介 | 适用场景 | 优缺点 | 代码示例 )

设计模式-简单工厂工厂方法模式抽象工厂模式详解

设计模式---工厂模式