Java设计模式——工厂模式详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_32625839/article/details/81631505

一、概述

1.1使用场景。
  在任何需要生成复杂对象的地方,都可以使用工厂方法模式, 直接用new可以完成的不需要用工厂模式。个人理解,重点就是这个复杂 (构造函数有很多参数)和是否可以直接用new。


二、工厂模式的五种写法

2.1简单的静态工厂模式。

/**
 * 图形抽象类
 */
public abstract class Shap {
    /**
     * 描述形状(画画)
     */
    public abstract void draw();
}
/**
 * 圆形类
 */
public class Circle extends Shap {
    public void draw() {
        System.out.println("这是圆形...");
    }
}
/**
 * 长方形类
 */
public class Rectangle extends Shap {
    public void draw() {
        System.out.println("这是长方形...");
    }
}
/**
 * 正方形类
 */
public class Square extends Shap {
    public void draw() {
        System.out.println("这是正方形...");
    }
}
/**
 * 简单静态工厂类
 */
public class SimpleStaticShapFactory {
    // 长方形
    public static final int TYPE_RECTANGLE = 1;
    // 正方形
    public static final int TYPE_SQUARE = 2;
    // 圆形
    public static final int TYPE_CIRCLE = 3;

    public static Shap createShap(int type) {
        switch (type) {
            case TYPE_RECTANGLE:
                return new Rectangle();
            case TYPE_SQUARE:
                return new Square();
            case TYPE_CIRCLE:
            default:
                return new Circle();
        }
    }
}
/**
 * 测试类
 */
public class Test {

    public static void main(String[] args) {
        // 创建实例
        Shap shap = SimpleStaticShapFactory.createShap(SimpleStaticShapFactory.TYPE_CIRCLE);
        shap.draw();
        // 创建实例
        Shap shap2 = SimpleStaticShapFactory.createShap(SimpleStaticShapFactory.TYPE_SQUARE);
        shap2.draw();
    }
}

// 运行结果
这是圆形...
这是正方形...

特点:
(1)它是一个具体的类,非接口抽象类。有一个重要的createShap()方法,利用if或者 switch创建产品并返回。
(2)createShap()方法通常是静态的,所以也称之为静态工厂。
缺点:
(1)扩展性差(我想增加一种图形,除了新增一个图形类,还需要修改工厂类方法)。
(2)不同的图形类需要不同额外参数的时候不支持。


2.2基于反射的简单工厂模式。

/**
 * 工厂类
 */
public class ReflectFactory {
    /**
     * @param clzzz Class对象
     * @return 实例对象
     */
    public static <T extends Shap> T createShap(Class<T> clzz) {
        T instance = null;
        try {
            instance = (T) Class.forName(clzz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;
    }
}
/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        //创建实例
        Shap shap = ReflectFactory.createShap(Rectangle.class);
        shap.draw();
        //创建实例
        Shap shap2 = ReflectFactory.createShap(Square.class);
        shap2.draw();
    }
}
// 运行结果
这是长方形...
这是正方形...

特点:
(1)它也是一个具体的类,非接口抽象类。但它的createShap()方法,是利用反射机制生成对象返回,好处是增加一种产品时,不需要修改createShap()的代码。

缺点:
(1)这种写法粗看牛逼,细想之下,不谈reflection的效率还有以下问题。
(2)个人觉得不好,因为Class.forName(clzz.getName()).newInstance()调用的是无参构造函数生成对象,它和new Object()是一样的性质,而工厂方法应该用于复杂对象的初始化 ,当需要调用有参的构造函数时便无能为力了,这样像为了工厂而工厂。
(3)不同的产品需要不同额外参数的时候 不支持。


2.3多方法静态工厂模式(常用)。

/**
 * 工厂类
 */
public class MultipartStaticFactory {
    /**
     * 创建圆形
     * @return Circle
     */
    public static Shap createCircle() {
        return new Circle();
    }
    /**
     * 创建长方形
     * @return Rectangle
     */
    public static Shap createRectangle() {
        return new Rectangle();
    }
    /**
     * 创建正方形
     * @return Square
     */
    public static Shap createSquare() {
        return new Square();
    }
}
/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        // 创建实例
        Shap shap = MultipartStaticFactory.createCircle();
        shap.draw();
        // 创建实例
        Shap shap2 = MultipartStaticFactory.createSquare();
        shap2.draw();
    }
}

//运行结果
这是圆形...
这是正方形...

特点:
(1)使用前面两张方法实现的工厂,都有一个缺点:不同的类需要不同额外参数的时候不支持。
(2) 如果使用时传递的type、Class出错,将不能得到正确的对象,容错率不高。 而多方法的工厂模式为不同产品,提供不同的生产方法,使用时需要哪种产品就调用该种产品的方法,使用方便、容错率高。
(3)这个例子可以感受到工厂方法的魅力了吧:方便创建同种类型的复杂参数对象。

此种设计模式还在Executors源码用到:
  Executor是生成各种线程池的工厂,其采用的便是多方法静态工厂模式。

/**
 * java.util.concurrent.Executors类部分源码
 */
public class Executors {
    /**
     * ThreadPoolExecutor方法有5个参数,有部分参数写法固定,而工厂类就只需要传入一个参数
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    /**
     * 假如JDK想增加创建ForkJoinPool类的方法,只想配置parallelism参数,便在类里增加一个如下的方法
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

2.4普通工厂模式。

/**
 * 抽象工厂类,生产图形
 */
public abstract class ShapFactory {
    abstract Shap createShap();
}
/**
 * 具体工厂子类,圆形工厂
 */
public class CircleFactory extends ShapFactory {
    @Override
    Shap createShap() {
        return new Circle();
    }
}
/**
 * 具体工厂子类,长方形工厂
 */
public class RectangleFactory extends ShapFactory {

    @Override
    Shap createShap() {
        return new Rectangle();
    }
}
/**
 * 具体工厂子类,正方形工厂
 */
public class SquareFactory extends ShapFactory {
    @Override
    Shap createShap() {
        return new Square();
    }
}
/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        // 创建圆形工厂
        ShapFactory shapFactory = new CircleFactory();
        // 创建圆形实例
        Shap circle = shapFactory.createShap();
        circle.draw();

        // 创建正方形工厂
        ShapFactory shapFactory2 = new SquareFactory();
        // 创建正方形实例
        Shap Square = shapFactory2.createShap();
        Square.draw();
    }
}
// 运行结果
这是圆形...
这是正方形...

特点:
(1)普通工厂就是把简单工厂【2.1简单的静态工厂模式】中具体的工厂类,划分成两层:抽象工厂层和具体的工厂子类层。
(2)可以看出,普通工厂模式特点:不仅仅做出来的产品要抽象, 工厂也应该需要抽象。
(3)工厂方法使一个产品类的实例化延迟到其具体工厂子类。
(4)工厂方法的好处就是更拥抱变化。当需求变化,只需要增删相应的类,不需要修改
(5)而简单工厂【2.1简单的静态工厂模式】需要修改工厂类的createShap()方法和具体图形类,多方法静态工厂【2.3多方法静态工厂模式】模式需要增加一个静态方法和具体图形类。
缺点:
(1) 引入抽象工厂层后,每次新增一个具体图形类,也要同时新增一个具体工厂类。


2.4抽象工厂模式。

/**
 * 饮料抽象类
 */
public abstract class Drink {
    /**
     * 描述每种饮料多少钱
     */
    public abstract void prices();
}
/**
 * 可乐类
 */
public class Cola extends Drink {
    @Override
    public void prices() {
        System.out.println("可口可乐3.0元...");
    }
}
/**
 * 牛奶类
 */
public class Milk extends Drink {
    @Override
    public void prices() {
        System.out.println("牛奶2.5元...");
    }
}
/**
 * 抽象工厂类
 */
public abstract class DrinkAndDraw {
    /**
     * 喝饮料
     * 
     * @return
     */
    public abstract Drink doDrink();
    /**
     * 画画
     * 
     * @return
     */
    public abstract Shap doDraw();
}
/**
 * 具体工厂类1
 */
public class FactoryOne extends DrinkAndDraw {
    /**
     * 喝可乐
     */
    @Override
    public Drink doDrink() {
        return new Cola();
    }
    /**
     * 画圆形
     */
    @Override
    public Shap doDraw() {
        return new Circle();
    }
}
/**
 * 具体工厂类2
 */
public class FactoryTwo extends DrinkAndDraw {
    /**
     * 喝牛奶
     */
    @Override
    public Drink doDrink() {
        return new Milk();
    }
    /**
     * 画正方形
     */
    @Override
    public Shap doDraw() {
        return new Square();
    }
}
/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        // 上幼儿园的小明一边喝可口可乐,一边在画圆形
        // 创建工厂1的实例,返回的方法是可乐实例的方法和圆形实例的方法
        DrinkAndDraw drinkAndDraw = new FactoryOne();
        // 调用可乐实例的喝可乐方法
        drinkAndDraw.doDrink().prices();
        // 调用圆形实例的画圆形方法
        drinkAndDraw.doDraw().draw();

        // 上幼儿园的小明一边喝牛奶,一边在画正方形
        DrinkAndDraw drinkAndDraw2 = new FactoryTwo();
        drinkAndDraw2.doDrink().prices();
        drinkAndDraw2.doDraw().draw();
    }
}
// 运行结果
可口可乐3.0...
这是圆形...
牛奶2.5...
这是正方形...

特点:
(1)将工厂也抽象了,在使用时,工厂和产品都是面向接口编程。
缺点:
(1)如果每次扩展新抽象类,比如读书抽象类,那就需要新加具体实现类如读小说还是读教科书,然后需要修改抽象工厂类,而后所有的具体工厂子类,都被牵连,需要同步被修改。

猜你喜欢

转载自blog.csdn.net/qq_32625839/article/details/81631505