【设计模式】创建型模式其三: 抽象工厂模式(Java版)

148 阅读6分钟

创建型模式其三:《抽象工厂模式》

为什么需要抽象工厂模式?

书接上文,我们讲解了工厂模式【一文通关】创建型模式其二: 工厂方法模式 - 掘金 (juejin.cn), 它的缺点也比较明显,每需要一个产品,都需要创建一个具体的工厂,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。

为此,抽象工厂模式就是来解决这个问题的: 一个工厂可以生产一系列产品(一族产品),极大减少了工厂类的数量。

模式动机

  • 当系统所提供的工厂生产的具体产品并不是一个简单的对象,而是多个位于不同产品等级结构、属于不同类型的具体产品时就可以使用抽象工厂模式
  • 抽象工厂模式是所有形式的工厂模式中最为抽象和最具一般性的一种形式

总之,就是指一个工厂生产具体的一系列具体产品,这样工厂类数量就减少了

image.png

搞懂抽象工厂模式的作用

现在来理解繁琐的定义

抽象工厂模式的定义

  • 又称为工具(Kit)模式
  • 抽象工厂模式中的具体工厂不只是创建一种产品,它负责创建一组产品
  • 当一个工厂等级结构可以创建出分属于不同产品等级结构的一个产品族中的所有对象时,抽象工厂模式比工厂方法模式更为简单、更有效率

抽象工厂模式结构: 抽象工厂模式包含以下4个角色:

  • AbstractFactory(抽象工厂)
  • ConcreteFactory(具体工厂)
  • AbstractProduct(抽象产品)
  • ConcreteProduct(具体产品)

概念及作用了解后,开始实战吧

某软件公司要开发一套界面皮肤库,可以对基于Java的桌面软件进行界面美化。用户在使用时可以通过菜单来选择皮肤,不同的皮肤将提供视觉效果不同的按钮、文本框、组合框等界面元素,例如春天(Spring)风格的皮肤将提供浅绿色的按钮、绿色边框的文本框和绿色边框的组合框,而夏天(Summer)风格的皮肤则提供浅蓝色的按钮、蓝色边框的文本框和蓝色边框的组合框,其结构示意图如图所示。 该皮肤库需要具备良好的灵活性和可扩展性,用户可以自由选择不同的皮肤,开发人员可以在不修改既有代码的基础上增加新的皮肤。 现使用抽象工厂模式来设计该界面皮肤库。

根据题意:这就是典型的一族产品

image.png

思路: 我们需要不同的风格的具体的工厂类,这些不同的具体工厂类需要继承一个抽象的工厂类。

抽象产品类

同一款不同风格的具体产品类应该是具有公共方法的,因此设计抽象(注意,这里的抽象是指抽象类或接口)产品类 // 抽象按钮类

public interface Button {
   public void display();
}

// 抽象组合框

public interface ComboBox {
   public void display();
}

// 抽象文本框

public interface TextField {
   public void display();
}

既然有了抽象产品类,现在实现具体产品类

具体产品类

// 春天的按钮

public class SpringButton implements Button {
   public void display() {
      System.out.println("显示浅绿色按钮。");
   }
}

// 春天的文本框

public class SpringTextField implements TextField {
   public void display() {
      System.out.println("显示绿色边框文本框。");
   }
}

// 春天的组合框

public class SpringComboBox implements ComboBox {
   public void display() {
      System.out.println("显示绿色边框组合框。");
   }
}

// 夏天的按钮

public class SummerButton implements Button {
   public void display() {
      System.out.println("显示浅蓝色按钮。");
   }  
}

// 夏天的文本框

public class SummerTextField implements TextField {
   public void display() {
      System.out.println("显示蓝色边框文本框。");
   }  
}

// 夏天的组合框

public class SummerComboBox implements ComboBox {
   public void display() {
      System.out.println("显示蓝色边框组合框。");
   }  
}

抽象工厂类

我们的抽象工厂里拥有创建按钮,文本框和组合框的功能,但是要交给具体工厂类去实现。因此,将其设为一个接口

public interface SkinFactory {
   public Button createButton();
   public TextField createTextField();
   public ComboBox createComboBox();
}

具体工厂类

既然有了抽象工厂类,那我们可以设计具体的工厂类:
思路: 实现抽象工厂的接口,根据自身的特点创建不同类型的具体产品类

// 春天类型的工厂

public class SpringSkinFactory implements SkinFactory {
   public Button createButton() {
      return new SpringButton();
   }

   public TextField createTextField() {
      return new SpringTextField();
   }

   public ComboBox createComboBox() {
      return new SpringComboBox();
   }
}

// 夏天类型的工厂

public class SummerSkinFactory implements SkinFactory {
   public Button createButton() {
      return new SummerButton();
   }

   public TextField createTextField() {
      return new SummerTextField();
   }

   public ComboBox createComboBox() {
      return new SummerComboBox();
   }
}

客户端类

我们的功能设计已经完成,现在可以具体的使用: 思路:客户端想要什么类型的工厂,就new什么类型的工厂,使用抽象工厂指着。

public class Client {
   public static void main(String args[]) {
        //使用抽象层定义
      SkinFactory factory;
      Button bt;
      TextField tf;
      ComboBox cb;
      factory = new SpringSkinFactory();
      bt = factory.createButton();
      tf = factory.createTextField();
      cb = factory.createComboBox();
      bt.display();
      tf.display();
      cb.display();
   }
}

输出:
显示浅绿色按钮。
显示绿色边框文本框。
显示绿色边框组合框。

上面已经是完整的功能,如果想要更进一步,可以看下面的。


但是我们有更好的办法,将选择写在配置文件中,这样客户端只用修改配置即可完成功能。

XML文件

XML文件里写具体的工厂类

<?xml version="1.0"?>
<config>
   <className>designpatterns.abstractfactory.SummerSkinFactory</className>
</config>

XML读取类

public class XMLUtil {
   //该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
   public static Object getBean() {
      try {
         //创建DOM文档对象
         DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
         DocumentBuilder builder = dFactory.newDocumentBuilder();
         Document doc;                    
         doc =builder.parse(new File("./config.xml")); 
      
         //获取包含类名的文本结点
         NodeList nl = doc.getElementsByTagName("className");
         Node classNode=nl.item(0).getFirstChild();
         String cName=classNode.getNodeValue();
        
         //通过类名生成实例对象并将其返回
         Class c=Class.forName(cName);
         Object obj=c.getConstructor().newInstance();
         return obj;
      }   
      catch(Exception e) {
         e.printStackTrace();
         return null;
      }
   }
}

修改客户端类

读取配置动态生成工厂类

public class Client {
   public static void main(String args[]) {
        //使用抽象层定义
      SkinFactory factory;
      Button bt;
      TextField tf;
      ComboBox cb;
      factory = (SkinFactory)XMLUtil.getBean();
      bt = factory.createButton();
      tf = factory.createTextField();
      cb = factory.createComboBox();
      bt.display();
      tf.display();
      cb.display();
   }
}

输出:
显示浅蓝色按钮。
显示蓝色边框文本框。
显示蓝色边框组合框。


完成代码撰写后,来看看抽象工厂的优缺点:

抽象工厂的优缺点

模式优点

  • 隔离了具体类的生成,使得客户端并不需要知道什么被创建
  • 当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
  • 增加新的产品族很方便,无须修改已有系统,这里符合开闭原则

模式缺点

  • 增加新的产品等级结构麻烦这句话意思是某个产品族增加其他组件,比如上面的的例子中增加一个输入框:需要修改抽象工厂,大改,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,这里违背了开闭原则

以上就是全部内容

下一篇,创建型模式其四:【一文通关】创建型模式其四: 建造者模式 - 掘金 (juejin.cn)](juejin.cn/post/722211…)