设计模式抽象工厂模式

通过实例说设计模式——抽象工厂模式

抽象工厂模式的 UML 图

工厂方法模式引入工厂等级结构,解决了简单工厂模式中工厂类职责过重的问题,但由于工厂方法模式中每个工厂只创建一类具体类的对象,这将会导致系统当中的工厂类过多,这势必会增加系统的开销。此时,我们可以考虑将一些相关的具体类组成一个“具体类族”,由同一个工厂来统一生产,这就是我们本文要说的“抽象工厂模式”的基本思想。

接着来看我们抽象方法模式一文中我们说的例子,我们现在有这么一个场景;现在用户的需求变多了,除了要求不同的形状之外,我们还要求创建不同颜色的形状,比如圆形,我们要求红色的圆形、赤橙黄绿青蓝紫等各种各样颜色的圆形。按照我们抽象方法模式的设计方案,那么我们就需要实现各种颜色的形状并实现这些颜色的形状对应的工厂类,这样一来我们系统中会增多非常多的工厂类。看起来如下:

//具体类的抽象接口
public interface Shape {
    //接口方法
}
//具体类Circle
public class Circle implements Shape {
    //接口方法实现
}
//具体类红色圆
public class RedCircle implements Shape {
    //接口方法实现
}
//...各种颜色的圆...
//具体类Triangle
public class Triangle implements Shape {
//接口方法实现
}
//具体类红色Triangle
public class RedTriangle implements Shape {
//接口方法实现
}
//...各种颜色的Triangle...
//工厂接口
public interface ShapeFactory {
    Shape getShape();
}
//CircleFactory
public class CircleFactory implements ShapeFactory {
    //返回实例
}
//RedCircleFactory
public class RedCircleFactory implements ShapeFactory {
    //返回实例
}
//...各种颜色的Circle的工厂...
//TriangleFactory 
public class TriangleFactory implements ShapeFactory {
    //返回实例
}
//RedTriangleFactory 
public class RedTriangleFactory implements ShapeFactory {
    //返回实例
}
//...各种颜色的Triangle的工厂...

那么我们有没有办法对当前的设计方案进行优化,减少一些工厂类的产生呢?我们下面对当前的系统做进一步抽象。以形状为类的级别、颜色为族来对当前系统作进一步改善。


这里写图片描述


首先我们要以形状为等级、颜色为族来对系统进行进一步抽象。

等级抽象

进行等级抽象我们需要将不同的形状声明为抽象类(等级划分)并实现公共的抽象接口(Shape),然后具体的实现类继承自对应的抽象类;

//形状公共接口
public interface Shape {
    void draw();
}
//圆形抽象类Circle
public abstract class Circle implements Shape {
    public abstract void draw();
}
//长方形抽象类Rectange
public abstract class Rectange implements Shape {
    public abstract void draw();
}
//其他图形抽象类... ...

具体的实现类继承自对应的抽象类,继承自不同的抽象类就相当于将类划分为不同的等级,如:

//具体颜色的Circle实现
public class BlueCircle extends Circle {
    @Override
    public void draw() {
        System.out.println("绘制蓝色的圆");
    }
}
public class RedCircle extends Circle {
    @Override
    public void draw() {
        System.out.println("绘制红色的圆");
    }
}

//具体颜色的Rectange实现
public class RedRectange extends Rectange{
    @Override
    public void draw() {
        System.out.println("绘制红色长方形");
    }
}
public class BlueRectange extends Rectange {
    @Override
    public void draw() {
        System.out.println("绘制蓝色长方形");
    }
}

具体类族抽象

具体类族的划分我们以颜色为基础,不同类族的对象我们通过对应的具体工厂来创建。所以首先我们需要定义一个抽象工厂,具体工厂(族)实现抽象工厂的方法来生成一组具体对象。

//抽象工厂ShapeFactory 
public interface ShapeFactory {
    Shape getCircle();
    Shape getRectange();
}

//RedShapeFactory(他所代表的是红色形状这一族)
public class RedShapeFactory implements ShapeFactory {
    @Override
    public Shape getCircle() {
        return new RedCircle();
    }

    @Override
    public Shape getRectange() {
        return new RedRectange();
    }
}

//BlueShapeFactory(他所代表的是兰色形状这一族)
public class BlueShapeFactory implements ShapeFactory {
    @Override
    public Shape getCircle() {
        return new BlueCircle();
    }

    @Override
    public Shape getRectange() {
        return new BlueRectange();
    }
}

//...其他族...

等级以及族类划分完成之后,我么的客户端在使用时只需要知道抽象工厂(ShapeFactory )以及具体工厂(如BlueShapeFactory )就可以获得指定族的所有形状。

public class TestDemo {
    public static void main(String[] args) {
        ShapeFactory redShapeFactory = new RedShapeFactory();
        Shape circle = redShapeFactory.getCircle();
        circle.draw();
        Shape rectangle = redShapeFactory.getRectange();
        rectangle.draw();

        ShapeFactory greenShapeFactory = new GreenShapeFactory();
        Shape greenCircle = greenShapeFactory.getCircle();
        greenCircle.draw();
    }
}

从上面的例子我们就可以知道,抽象工厂模式为创建一组对象提供了一种解决方案。 与工厂方法模式相比, 抽象工厂模式中的具体工厂不只是创建一种具体对象, 它负责创建一组(族)具体对象。

抽象工厂模式定义(Abstract Factory Pattern)

提供一个创建一系列相关或者相互依赖对象的接口,而无需知道他们的具体类,抽象工厂模式也称Kit模式,它属于类创建型模式。

在抽象工厂模式中,每个具体工厂都提供了多个工厂方法用于创建多种不同类型的具体对象,这些被创建的对象就构成一个族。

抽象工厂模式包含的角色

  • 抽象工厂:声明一组用于创建一族产品的方法,每个方法对应一种对象;在抽象工厂中声明了多个工厂方法, 用于创建不同类型的对象, 抽象工厂可以是接口, 也可以是抽象类或者具体类,具体实现可参考上例中的ShapeFactory;

  • 具体工厂:具体工厂实现了抽象工厂,每个工厂方法返回一个具体对象,一个具体工厂所创建的具体对象构成一个族。具体实现可参考上例中的RedShapeFactory;

  • 抽象类接口:提供一组所有类都具有的业务方法。

  • 抽象类:用于实现抽象接口所定义的业务方法,但是该角色对于抽象接口定义的方法只做抽象实现,即所有实现都被定义为抽象方法,最终的具体实现全部交给具体类实现。引入该角色主要是为了根据声明不同的抽象类,将类区分为不同的等级结构。

  • 具体类:该角色继承抽象类,主要用于实现抽象类中声明的抽象方法,完成不同等级结构,不同族的业务方法的具体实现。

根据我们上面对抽象工厂模式的描述,我们大致能够明白,如果我们在上例的基础上想要增加绿色或者其他颜色的图形时,我们只需要增加一个绿色产品工厂(GreenShapeFactory),同时继承不同的图形抽象类实现不同颜色的图形具体类即可,看起来如下:

//GreenShapeFactory
public class GreenShapeFactory implements ShapeFactory {
    @Override
    public Shape getCircle() {
        return new GreenCircle();
    }

    @Override
    public Shape getRectange() {
        return new GreenRectange();
    }

}

//GreenRectange 
public class GreenRectange extends Rectange {
    @Override
    public void draw() {
        System.out.println("绘制绿色长方形");
    }
}

//GreenCircle
public class GreenCircle extends Circle{
    @Override
    public void draw() {
        System.out.println("绘制绿色圆");
    }
}

我们增加这些拓展,并不会影响到原来的代码,这看起来是符合“开闭原则”的;但是我们再深入思考一下,如果我们现在想增加一个新的图形,比如说三角形(Triangle)。而对于我们目前所做的设计来讲,我们将三角形(Triangle)放到任何一个等级机构(即继承自任何一个图形抽象类,如Circle)都显得有些格格不入。但是我们如果在系统之中增加一个三角形(Triangle)的抽象类并实现不同颜色的三角形(Triangle)类之后我们会发现,我们需要修改所有的抽象工厂,这并不符合“开闭原则”。即在抽象工厂模式中,我们只能够对族进行拓展,在对族进行拓展的情况下,时符合“开闭原则”的,如上例中增加不同颜色的指定图形;但是对类的等级结构做拓展,是不符合“开闭原则”的;

因此使用抽象工厂模式要求设计人员在设计之初就能够全面考虑, 不会在设计完成之后向系统中增加新的等级结构, 也不会删除已有的等级结构, 否则将会导致系统出现较大的修改, 为后续维护工作带来诸多麻烦。

小结

抽象工厂模式是工厂方法模式的进一步延伸, 由于它提供了功能更为强大的工厂类并且具备较好的可扩展性, 在软件开发中得以广泛应用, 尤其是在一些框架和API类库的设计中, 例如在Java语言的AWT( 抽象窗口工具包) 中就使用了抽象工厂模式, 它使用抽象工厂模式来实现在不同的操作系统中应用程序呈现与所在操作系统一致的外观界面。 抽象工厂模式也是在软件开发中最常用的设计模式之一。

优点

  • 抽象工厂模式隔离了具体类的生成, 使得客户并不需要知道什么被创建。 由于这种隔离,更换一个具体工厂就变得相对容易, 所有的具体工厂都实现了抽象工厂中定义的那些公共接口, 因此只需改变具体工厂的实例, 就可以在某种程度上改变整个软件系统的行为。

  • 当一个族中的多个对象被设计成一起工作时, 它能够保证客户端始终只使用同一个族中的对象。

  • 增加新的族很方便, 无须修改已有系统, 符合“开闭原则”。

缺点

  • 增加新的等级结构麻烦, 需要对原有系统进行较大的修改, 甚至需要修改抽象层代码,这显然会带来较大的不便, 违背了“开闭原则”。

使用场景

  • 一个系统不应当依赖于具体类实例如何被创建、 组合和表达的细节, 这对于所有类型的工厂模式都是很重要的, 用户无须关心对象的创建过程, 将对象的创建和使用解耦;

  • 系统中有多于一个的族, 而每次只使用其中某一族。 可以通过配置文件等方式来使得用户可以动态改变族, 也可以很方便地增加新的族。

  • 属于同一个族的对象将在一起使用, 这一约束必须在系统的设计中体现出来。 同一个族中的对象可以是没有任何关系的对象, 但是它们都具有一些共同的约束, 如同一操作系统下的按钮和文本框, 按钮与文本框之间没有直接关系, 但它们都是属于某一操作系统的, 此时具有一个共同的约束条件: 操作系统的类型。

  • 等级结构稳定, 设计完成之后, 不会向系统中增加新的等级结构或者删除已有的等级结构。

原文地址:https://www.cnblogs.com/grj001/p/12223632.html