天天看點

設計模式—【01】單例、原型、工廠方法、抽象工廠模式

環境

Java:1.8.0_202-b08

dependency:

  • lombok 1.18.6
  • Guava 27.0.1-jre
  • slf4j-log4j12 1.7.25

    Github Repositories: https://github.com/ilssio/design-pattern

1. 單例模式

a). 定義:

Java中單例模式定義:“一個類有且僅有一個執行個體,并且自行執行個體化向整個系統提供。”

b). 實作

請看注釋

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonThree
 * @date : 2019-04-11 15:42
 * @version: : 1.0
 */
public class SingletonOne {

    private final static SingletonOne INSTANCE = new SingletonOne();

    private SingletonOne(){}

    public static SingletonOne getInstance(){
        return INSTANCE;
    }

    /**
     * 第一種:餓漢式 使用靜态常量直接new
     *
     * 在類加載的時候,完成執行個體,可以避免線程同步問題
     */
}
           

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonThree
 * @date : 2019-04-11 15:54
 * @version: : 1.0
 */
public class SingletonTwo {
    private final static SingletonTwo INSTANCE;

    static {
        INSTANCE = new SingletonTwo();
    }

    private SingletonTwo() {}

    public static SingletonTwo getInstance() {
        return INSTANCE;
    }

    /**
     * 第二種:餓漢式 使用靜态代碼塊
     *
     * 和第一種類似
     */
}
           

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonThree
 * @date : 2019-04-11 16:01
 * @version: : 1.0
 */
public class SingletonThree {
    private static SingletonThree SingletonThree;

    private SingletonThree() {}

    public static SingletonThree getInstance() {
        if (SingletonThree == null) {
            SingletonThree = new SingletonThree();
        }
        return SingletonThree;
    }

    /**
     * 第三種:懶漢式 通過getInstance方法中空判斷來實作
     *
     * 注:此種方法線程不安全,多線程情況 不推薦使用。
     *
     */
}
           

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonFour
 * @date : 2019-04-11 16:11
 * @version: : 1.0
 */
public class SingletonFour {

    private static SingletonFour INSTANCE;

    private SingletonFour() {
    }

    public static synchronized SingletonFour getInstance() {
        if (null == INSTANCE) {
            INSTANCE = new SingletonFour();
        }
        return INSTANCE;
    }

    /**
     * 第四種:懶漢式 使用synchronized同步鎖實作
     *
     * 因為使用了方法級鎖,效率偏低。
     */
}
           

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonFive
 * @date : 2019-04-11 16:17
 * @version: : 1.0
 */
public class SingletonFive {

    private static SingletonFive INSTANCE;

    private SingletonFive() { }

    public static SingletonFive getInstance() {
        if (null == INSTANCE) {
            synchronized (SingletonFive.class) {
                if (null == INSTANCE) {
                    INSTANCE = new SingletonFive();
                }
            }
        }
        return INSTANCE;
    }

    /**
     * 第五種 懶漢式 使用double-check(雙重檢查機制)
     *
     * 延遲加載,線程安全。 推薦使用
     */
}
           

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonSix
 * @date : 2019-04-12 09:27
 * @version: : 1.0
 */
public class SingletonSix {
    private static class Singleton {
        private static SingletonSix INSTANCE = new SingletonSix();
    }

    public static SingletonSix getInstance() {
        return Singleton.INSTANCE;
    }
    /**
     * 第六種 靜态内部類
     *
     * 采用類裝載的機制來保證初始化執行個體時隻有一個線程。
     * 靜态内部類方式在Singleton類被裝載時并不會立即執行個體化,
     * 而是在需要執行個體化時,調用getInstance方法,才會裝載Singleton類,
     * 進而完成SingletonSix的執行個體化。
     */
}
           

package io.ilss.pattern.singleton;

/**
 * @author : feng
 * @description: SingletonSeven
 * @date : 2019-04-12 09:31
 * @version: : 1.0
 */
public class SingletonSeven {
    public String string = "test";
    // 内部枚舉類
    private enum EnumSingleton{
        // 規範要求必須有注釋。。請忽略這個注釋,好吧 既然你看完了,那就再看兩眼?
        Singleton;
        private SingletonSeven instance;

        // 枚舉類的構造方法在類加載時被執行個體化
        EnumSingleton(){
            instance = new SingletonSeven();
        }
        private SingletonSeven getInstance(){
            return instance;
        }
    }
    public static SingletonSeven getInstance() {
        return EnumSingleton.Singleton.getInstance();
    }


    /**
     * 第七種:通過内部枚舉類實作
     *
     * Java保證了所有枚舉的構造方法隻會被執行一次,是以無論多少線程調用,都隻會産生一個執行個體。
     */
}
           

2. 原型模式

a). 定義

原型模式就是用原型執行個體指定建立對象的種類,并且通過複制這些原型建立新的對象。

淺拷貝:使用一個已知執行個體對新建立執行個體的成員變量逐個指派,這個方式被稱為淺拷貝。

深拷貝:當一個類的拷貝構造方法,不僅要複制對象的所有非引用成員變量值,還要為引用類型的成員變量建立新的執行個體,并且初始化為形式參數執行個體值。

關于淺拷貝和深拷貝 可以看 http://www.cnblogs.com/chenssy/p/3308489.html

b). 實作

package io.ilss.pattern.prototype;

import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: SomeObject
 * @date : 2019-04-12 14:11
 * @version: : 1.0
 */
@Getter
@Setter
@Slf4j
public class SomeObject implements Cloneable {
    private int flag = 1;
    private String msg = "hello world!";

    /**
     * Java中的Object類提供了淺克隆的clone()方法,
     * 具體原型類隻要實作 Cloneable 接口就可實作對象的淺克隆
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        SomeObject object1 = new SomeObject();
        SomeObject object2 = (SomeObject) object1.clone();
        SomeObject object3 = (SomeObject) object1.clone();
        object3.setFlag(2);
        log.debug(" object 1 : {} , hashCode() : {} , toString() : {} ", object1, object1.hashCode(), object1.getMsg() + " " + object1.getFlag());
        log.debug(" object 2 : {} , hashCode() : {} , toString() : {} ", object2, object2.hashCode(), object2.getMsg() + " " + object2.getFlag());
        log.debug(" object 3 : {} , hashCode() : {} , toString() : {} ", object3, object3.hashCode(), object3.getMsg() + " " + object3.getFlag());

    }
}
           

②用帶原型管理器的原型模式來生成包含“圓”和“正方形”等圖形的原型

package io.ilss.pattern.prototype;

/**
 * @author : feng
 * @description: Shape
 * @date : 2019-04-12 14:42
 * @version: : 1.0
 */
interface Shape extends Cloneable {
    /**
     * 拷貝
     * @return Object
     */
    Object clone();

    /**
     * 計算面積
     * @return area
     */
    double computeArea();

}
           
package io.ilss.pattern.prototype;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.Scanner;

/**
 * @author : feng
 * @description: Circle
 * @date : 2019-04-12 14:44
 * @version: : 1.0
 */

@Getter
@Setter
@Slf4j
public class Circle implements Shape {
    double radius = 1.0;
    @Override
    public Object clone() {
        Circle circle = null;
        try {
            circle = (Circle) super.clone();
        } catch (CloneNotSupportedException e) {
            log.error("Clone Circle class failed!");
        }
        return circle;
    }

    @Override
    public double computeArea() {
        return Math.PI * radius * radius;
    }
}
           
package io.ilss.pattern.prototype;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: Square
 * @date : 2019-04-12 14:48
 * @version: : 1.0
 */
@Getter
@Setter
@Slf4j
class Square implements Shape {
    private double length = 1.0;
    @Override
    public Object clone() {
        Square square = null;
        try {
            square = (Square) super.clone();
        } catch (CloneNotSupportedException e) {
            log.error("Clone Square class failed!");
        }
        return square;
    }

    @Override
    public double computeArea() {
        return length * length;
    }

}
           
package io.ilss.pattern.prototype;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * @author : feng
 * @description: PrototypeManager
 * @date : 2019-04-12 14:42
 * @version: : 1.0
 */
@Slf4j
class PrototypeManager {
    private Map<String, Shape> map = Maps.newHashMap();

    public PrototypeManager() {
        map.put("Circle", new Circle());
        map.put("Square", new Square());
    }

    public void addshape(String key, Shape obj) {
        map.put(key, obj);
    }

    public Shape getShape(String key) {
        Shape temp = map.get(key);
        return (Shape) temp.clone();
    }

    public static void main(String[] args) {
        PrototypeManager manager = new PrototypeManager();
        Shape object1 = manager.getShape("Circle");
        log.debug(" {} ", object1.computeArea());

        Shape object2 = manager.getShape("Square");
        log.debug(" {} ", object2.computeArea());

        Circle circle = (Circle) manager.getShape("Circle");
        circle.setRadius(2.2);
        Shape object3 = circle;
        log.debug(" {} ", object3.computeArea());

    }
}
           

3. 工廠方法模式

a). 定義

工廠方法模式是簡單工廠模式的衍生,解決了許多簡單工廠模式的問題。首先完全實作‘開-閉 原則’,實作了可擴充。其次更複雜的層次結構,可以應用于産品結果複雜的場合。

簡單工廠模式是屬于建立型模式。簡單工廠模式是由一個工廠對象決定建立出哪一種産品類的執行個體。
簡單工廠模式是工廠模式中最簡單實用的模式,可以了解為是不同工廠模式的一個特殊實作。
簡單工廠模式又稱靜态工廠方法模式。它存在的目的很簡單:定義一個用于建立對象的接口。
           

工廠方法模式中考慮的是一類産品的生産,如畜牧場隻養動物、電視機廠隻生産電視機、計算機軟體學院隻培養計算機軟體專業的學生

工廠方法模式的主要角色如下。

  • 抽象工廠(Abstract Factory):提供了建立産品的接口,調用者通過它通路具體工廠的工廠方法 newProduct() 來建立産品。
  • 具體工廠(ConcreteFactory):主要是實作抽象工廠中的抽象方法,完成具體産品的建立。
  • 抽象産品(Product):定義了産品的規範,描述了産品的主要特性和功能。
  • 具體産品(ConcreteProduct):實作了抽象産品角色所定義的接口,由具體工廠來建立,它同具體工廠之間一一對應。
    設計模式—【01】單例、原型、工廠方法、抽象工廠模式

b). 實作

Product

package io.ilss.pattern.factorymethod;

/**
 * @author : feng
 * @description: Product 抽象産品
 * @date : 2019-04-12 16:10
 * @version: : 1.0
 */
interface Product {
    public void show();
}
ConcreteProduct

package io.ilss.pattern.factorymethod;

import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: ConcreteProductOne 産品1
 * @date : 2019-04-12 16:11
 * @version: : 1.0
 */
@Slf4j
class ConcreteProductOne implements Product {
    private String name = "One";
    @Override
    public void show() {
        log.info("Product {} show()", name);
    }
}
           
package io.ilss.pattern.factorymethod;

import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: ConcreteProductOne 産品2
 * @date : 2019-04-12 16:11
 * @version: : 1.0
 */
@Slf4j
class ConcreteProductTwo implements Product {
    private String name = "Two";
    @Override
    public void show() {
        log.info("Product {} show()", name);
    }
}
           

AbstractFactory

package io.ilss.pattern.factorymethod;

/**
 * @author : feng
 * @description: ProductAbstractFactory 抽象工廠
 * @date : 2019-04-12 16:13
 * @version: : 1.0
 */
public interface ProductAbstractFactory {

    /**
     * 生産産品
     * @return Product
     */
    Product newProduct();

}
           

ConcreteFactory

package io.ilss.pattern.factorymethod;

/**
 * @author : feng
 * @description: ProductConcreteOneFactory 具體工廠1
 * @date : 2019-04-12 16:15
 * @version: : 1.0
 */
public class ProductConcreteOneFactory implements ProductAbstractFactory {

    @Override
    public Product newProduct() {
        return new ConcreteProductOne();
    }
}
           
package io.ilss.pattern.factorymethod;

/**
 * @author : feng
 * @description: ProductConcreteTwoFactory 具體工廠2
 * @date : 2019-04-12 16:16
 * @version: : 1.0
 */
public class ProductConcreteTwoFactory implements ProductAbstractFactory {
    @Override
    public Product newProduct() {
        return new ConcreteProductTwo();
    }
}
           

4. 抽象工廠模式

a). 定義

  • 抽象工廠模式是所有形态的工廠模式中最為抽象和最具一般性的一種形态。抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向用戶端提供一個接口,使用戶端在不必指定産品的具體的情況下,建立多個産品族中的産品對象。根據裡氏替換原則,任何接受父類型的地方,都應當能夠接受子類型。是以,實際上系統所需要的,僅僅是類型與這些抽象産品角色相同的一些執行個體,而不是這些抽象産品的執行個體。換言之,也就是這些抽象産品的具體子類的執行個體。工廠類負責建立抽象産品的具體子類的執行個體。

裡氏替換原則:

  • 裡氏替換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 裡氏替換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承複用的基石,隻有當衍生類可以替換掉基類,軟體機關的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。

如此,問題産生了:“我們如何去度量繼承關系的品質?”

Liskov于1987年提出了一個關于繼承的原則** “Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確定超類所擁有的性質在子類中仍然成立。” **也就是說,當一個子類的執行個體應該能夠替換任何其超類的執行個體時,它們之間才具有is-A關系。

該原則稱為Liskov Substitution Principle——裡氏替換原則。

  • 抽象工廠是一種為通路類提供一個建立一組相關或互相依賴對象的接口,且通路類無須指定所要産品的具體類就能得到同族的不同等級的産品的模式結構。
  • 抽象工廠模式是工廠方法模式的更新版本,工廠方法模式隻生産一個等級的産品,而抽象工廠模式可生産多個等級的産品。

b). 實作

設計模式—【01】單例、原型、工廠方法、抽象工廠模式
package io.ilss.pattern.abstractfactory;

/**
 * @author : feng
 * @description: ProductOne 抽象産品 1
 * @date : 2019-04-12 16:10
 * @version: : 1.0
 */
interface ProductOne {
    public void show();
}


package io.ilss.pattern.abstractfactory;

/**
 * @author : feng
 * @description: ProductOne 抽象産品 2
 * @date : 2019-04-12 16:10
 * @version: : 1.0
 */
interface ProductTwo {
    public void show();
}
           
package io.ilss.pattern.abstractfactory;

import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: ConcreteProductOneOne 産品1 廠商1
 * @date : 2019-04-12 16:46
 * @version: : 1.0
 */
@Slf4j
public class ConcreteProductOneOne implements ProductOne {
    private String name = "OneOne";
    @Override
    public void show() {
        log.info("Product {} show()", name);
    }
}
           
package io.ilss.pattern.abstractfactory;

import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: ConcreteProductOneTwo 産品1 廠商2
 * @date : 2019-04-12 16:46
 * @version: : 1.0
 */
@Slf4j
public class ConcreteProductOneTwo implements ProductOne {
    private String name = "OneTwo";
    @Override
    public void show() {
        log.info("Product {} show()", name);

    }
}
           
package io.ilss.pattern.abstractfactory;

import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: ConcreteProductTwoOne 産品2 廠商1
 * @date : 2019-04-12 16:46
 * @version: : 1.0
 */
@Slf4j
public class ConcreteProductTwoOne implements ProductTwo {
    private String name = "TwoOne";
    @Override
    public void show() {
        log.info("Product {} show()", name);

    }
}
           
package io.ilss.pattern.abstractfactory;

import lombok.extern.slf4j.Slf4j;

/**
 * @author : feng
 * @description: ConcreteProductTwoTwo 産品2 廠商2
 * @date : 2019-04-12 16:46
 * @version: : 1.0
 */
@Slf4j
public class ConcreteProductTwoTwo implements ProductTwo {
    private String name = "TwoTwo";
    @Override
    public void show() {
        log.info("Product {} show()", name);

    }
}
           
package io.ilss.pattern.abstractfactory;

/**
 * @author : feng
 * @description: AbstractFactory 抽象工廠
 * @date : 2019-04-12 16:43
 * @version: : 1.0
 */
interface AbstractFactory
{
    public ProductOne newProductOne();
    public ProductTwo newProductTwo();
}
           
package io.ilss.pattern.abstractfactory;

/**
 * @author : feng
 * @description: ProductConcreteOneFactory 工廠1
 * @date : 2019-04-12 16:44
 * @version: : 1.0
 */
public class ProductConcreteOneFactory {
    public ProductOne newProductOne() {
        return new ConcreteProductOneOne();
    }

    public ProductTwo newProductTwo() {
        return new ConcreteProductTwoOne();
    }
}
           
package io.ilss.pattern.abstractfactory;

/**
 * @author : feng
 * @description: ProductConcreteTwoFactory 工廠2
 * @date : 2019-04-12 16:44
 * @version: : 1.0
 */
public class ProductConcreteTwoFactory {
    public ProductOne newProductOne() {
        return new ConcreteProductOneTwo();
    }

    public ProductTwo newProductTwo() {
        return new ConcreteProductTwoTwo();
    }
}
           

參考文章

  1. http://c.biancheng.net/view/1343.html
  2. https://baike.baidu.com/item/原型模式/4941014?fr=aladdin
  3. https://www.cnblogs.com/zhaoyan001/p/6365064.html
  4. http://www.cnblogs.com/garryfu/p/7976546.html
  5. http://c.biancheng.net/view/1348.html
  6. https://baike.baidu.com/item/工廠方法模式/2361103?fr=aladdin
  7. http://www.cnblogs.com/forlina/archive/2011/06/21/2086114.html
  8. https://baike.baidu.com/item/抽象工廠模式
  9. http://c.biancheng.net/view/1351.html
  10. https://www.jianshu.com/p/7deb64f902db