寫在前面:
- 你好,歡迎關注!
- 我熱愛技術,熱愛分享,熱愛生活, 我始終相信:技術是開源的,知識是共享的!
- 博客裏面的內容大部分均爲原創,是自己日常的學習記錄和總結,便於自己在後面的時間裏回顧,當然也是希望可以分享 自己的知識。如果你覺得還可以的話不妨關注一下,我們共同進步!
- 個人除了分享博客之外,也喜歡看書,寫一點日常雜文和心情分享,如果你感興趣,也可以關注關注!
- 公衆號:傲驕鹿先生
目錄
四、 建造者模式(Build Pattern)
-
Director: 指揮者類,用於統一組裝流程
-
Builder:抽象Builder類,規範產品的組建,一般是由子類實現。
-
ConcreteBulider: 抽象Builder類的實現類,實現抽象Builder類定義的所有方法,並且返回一個組建好的對象
-
Product: 產品類
public class Computer {
private String mCpu;
private String mMainboard;
private String mRam;
public void setmCpu(String mCpu) {
this.mCpu = mCpu;
}
public void setmMainboard(String mMainboard) {
this.mMainboard = mMainboard;
}
public void setmRam(String mRam) {
this.mRam = mRam;
}
}
public abstract class Builder {
public abstract void buildCpu(String cpu);
public abstract void buildMainboard(String mainboard);
public abstract void buildRam(String ram);
public abstract Computer create();
}
(3)具體Builder類
商家實現了抽象的Builder類,MoonComputerBuilder類用於組裝電腦:
public class MoonComputerBuilder extends Builder {
private Computer mComputer = new Computer();
@Override public void buildCpu(String cpu) {
mComputer.setmCpu(cpu);
}
@Override
public void buildMainboard(String mainboard) {
mComputer.setmMainboard(mainboard);
}
@Override
public void buildRam(String ram) {
mComputer.setmRam(ram);
}
@Override
public Computer create() {
return mComputer;
}
}
(4)具體指揮者(Director)類
public class Direcror {
Builder mBuild=null;
public Direcror(Builder build){
this.mBuild=build;
}
public Computer CreateComputer(String cpu,String mainboard,String ram){
//規範建造流程
this.mBuild.buildMainboard(mainboard);
this.mBuild.buildCpu(cpu);
this.mBuild.buildRam(ram);
return mBuild.create();
}
}
(5)客戶端調用指揮者(Director)類
public class CreatComputer {
public static void main(String[]args){
Builder mBuilder=new MoonComputerBuilder();
Direcror mDirecror=new Direcror(mBuilder);
//組裝電腦
mDirecror.CreateComputer("i7-6700","華擎玩家至尊","三星DDR4");
}
}
3、優點和缺點
(1)優點
-
使用建造者模式可以使客戶端不必知道產品內部組成的細節。
-
具體的建造者類之間是相互獨立的,容易擴展。
-
由於具體的建造者是獨立的,因此可以對建造過程逐步細化,而不對其他的模塊產生任何影響。
(2)缺點
-
產生多餘的Build對象以及Dirextor類。
4、適用場景
-
當創建複雜對象的算法應該獨立於該對象的組成部分以及它們的裝配方式時。
-
相同的方法,不同的執行順序,產生不同的事件結果時。
-
多個部件或零件,都可以裝配到一個對象中,但是產生的運行結果又不相同時。
-
產品類非常複雜,或者產品類中的調用順序不同產生了不同的效能。
-
創建一些複雜的對象時,這些對象的內部組成構件間的建造順序是穩定的,但是對象的內部組成構件面臨着複雜的變化。
五、 單例模式(Singleton Pattern)
-
構造函數不對外開放---Private
-
通過一個靜態方法或者枚舉返回單利對象
-
確保單例類的對象有且只有一個,尤其是在多線程環境下。
-
確保單例類對象在反序列化時不會重新構建對象。
-
JVM在類的初始化階段(即 在Class被加載後、被線程使用前),會執行類的初始化
-
在執行類的初始化期間,JVM會去獲取一個鎖。這個鎖可以同步多個線程對同一個類的初始化
public class HungrySingleton {
private static final HungrySingleton mSingleton= new HungrySingleton();
//私有構造函數
private HungrySingleton(){
}
//公有的靜態函數,對外暴露獲取單例對象的接口
public static HungrySingleton getSingleton(){
return mSingleton;
}
}
(2)枚舉類型
public enum Singleton{
//定義1個枚舉的元素,即爲單例類的1個實例
INSTANCE;
// 隱藏了1個空的、私有的 構造方法
// private Singleton () {}
}
// 獲取單例的方式:
Singleton singleton = Singleton.INSTANCE;
class Singleton {
// 1. 類加載時,先不自動創建單例
// 即,將單例的引用先賦值爲 Null
private static Singleton ourInstance = null;
// 2. 構造函數 設置爲 私有權限
// 原因:禁止他人創建實例
private Singleton() {
}
// 3. 需要時才手動調用 newInstance() 創建 單例
public static Singleton newInstance() {
// 先判斷單例是否爲空,以避免重複創建
if( ourInstance == null){
ourInstance = new Singleton();
}
return ourInstance;
}
}
基礎實現的懶漢式是線程不安全的,具體原因如下:
-
即,getInstance()方法塊只能運行在1個線程中
-
若該段代碼已在1個線程中運行,另外1個線程試圖運行該塊代碼,則 會被阻塞而一直等待
-
而在這個線程安全的方法裏我們實現了單例的創建,保證了多線程模式下 單例對象的唯一性
// 寫法1
class Singleton {
// 1. 類加載時,先不自動創建單例
// 即,將單例的引用先賦值爲 Null
private static Singleton ourInstance = null;
// 2. 構造函數 設置爲 私有權限
// 原因:禁止他人創建實例
private Singleton() {
}
// 3. 加入同步鎖
public static synchronized Singleton getInstance(){
// 先判斷單例是否爲空,以避免重複創建
if ( ourInstance == null )
ourInstance = new Singleton();
return ourInstance;
}
}
// 寫法2// 該寫法的作用與上述寫法作用相同,只是寫法有所區別
class Singleton{
private static Singleton instance = null;
private Singleton(){
}
public static Singleton getInstance(){
// 加入同步鎖
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton();
}
return instance;
}
}
(3)懶漢式——雙重鎖優化
class Singleton {
private static Singleton ourInstance = null;
private Singleton() {}
public static Singleton newInstance() {
// 加入雙重校驗鎖
// 校驗鎖1:第1個if
if( ourInstance == null){ // ①
synchronized (Singleton.class){ // ②
// 校驗鎖2:第2個 if
if( ourInstance == null){
ourInstance = new Singleton();
}
}
}
return ourInstance;
}
}
檢驗鎖1: 第一個if
-
作用:若單例已創建,則直接返回已創建的單例,無需再執行加鎖操作。即直接跳到執行 return ourInstance
-
作用:防止多次創建單例問題
-
原理:
-
線程A調用newInstance(),當運行到②位置時,此時線程B也調用了newInstance()
-
因線程A並沒有執行instance = new Singleton();,此時instance仍爲空,因此線程B能突破第1層if 判斷,運行到①位置等待synchronized中的A線程執行完畢
-
當線程A釋放同步鎖時,單例已創建,即instance已非空
-
此時線程B 從①開始執行到位置②。此時第2層if判斷 = 爲空(單例已創建),因此也不會創建多餘的實例
-
public class StaticInnerSingleton {
private StaticInnerSingleton(){}
public static StaticInnerSingleton getInstance(){
return InstanceHolder .mStaticInnerSingleton;
}
//靜態內部類
private static class InstanceHolder {
private static StaticInnerSingleton mStaticInnerSingleton=new StaticInnerSingleton();
}
}
JVM在類的初始化階段(即在Class被加載後,且被線程使用之前),會執行類的初始化。在執行類的初始化期間,JVM會去獲取一個鎖。這個鎖可以同步多個線程對同一個類的初始化。
public class SingletonManager {
private static Map<String, Object> objectMap = new HashMap<>();
private SingletonManager() {
}
public static void registerService(String key, Object instance) {
if (!objectMap.containsKey(key)) {
objectMap.put(key, instance);
}
}
public static Object getService(String key) {
return objectMap.get(key);
}
}
在程序的初始,將多種單例類型注入到一個統一的管理類中,在使用時根據key獲取對象對應類型的對象。
public class SerializableSingleton implements Serializable {
private static final long serialVersionUID=0L;
private static final SerializableSingleton INSTANCE =new SerializableSingleton();
private SerializableSingleton(){}
public static SerializableSingleton getSerializableSingleton(){
return INSTANCE;
}
private Object readResolve() throws ObjectStreamException{
return INSTANCE;
}
}
readResolve方法中將單例對象返回,而不是重新生成一個新的對象。而對於枚舉則不存在這個問題。
六、原型模式( Prototype Pattern)
public interface Prototype{
/**
* 克隆自身的方法
* @return 一個從自身克隆出來的對象
*/
public Object clone();
}
具體原型:
public class ConcretePrototype1 implements Prototype {
public Prototype clone(){
//最簡單的克隆,新建一個自身對象,由於沒有屬性就不再複製值了
Prototype prototype = new ConcretePrototype1();
return prototype;
}
}
public class ConcretePrototype2 implements Prototype {
public Prototype clone(){
//最簡單的克隆,新建一個自身對象,由於沒有屬性就不再複製值了
Prototype prototype = new ConcretePrototype2();
return prototype;
}
}
客戶端角色:
public class Client {
//持有需要使用的原型接口對象
private Prototype prototype;
//構造方法,傳入需要使用的原型接口對象
public Client(Prototype prototype){
this.prototype = prototype;
}
public void operation(Prototype example){
//需要創建原型接口的對象
Prototype copyPrototype = prototype.clone();
}
}
2、登記形式的原型模式
作爲原型模式的第二種形式,它多了一個原型管理器(PrototypeManager)角色,該角色的作用是:創建具體原型類的對象,並記錄每一個被創建的對象。
(2)簡單實現
抽象原型角色:
public interface Prototype{
public Prototype clone();
public String getName();
public void setName(String name);
}
具體原型角色:
public class ConcretePrototype1 implements Prototype {
private String name;
public Prototype clone(){
ConcretePrototype1 prototype = new ConcretePrototype1();
prototype.setName(this.name);
return prototype;
}
public String toString(){
return "Now in Prototype1 , name = " + this.name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}
public class ConcretePrototype2 implements Prototype {
private String name;
public Prototype clone(){
ConcretePrototype2 prototype = new ConcretePrototype2();
prototype.setName(this.name);
return prototype;
}
public String toString(){
return "Now in Prototype2 , name = " + this.name;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
}
原型管理器角色保持一個聚集,作爲對所有原型對象的登記,這個角色提供必要的方法,供外界增加新的原型對象和取得已經登記過的原型對象。
public class PrototypeManager {
/**
* 用來記錄原型的編號和原型實例的對應關係
*/
private static Map<String,Prototype> map = new HashMap<String,Prototype>();
/**
* 私有化構造方法,避免外部創建實例
*/
private PrototypeManager(){}
/**
* 向原型管理器裏面添加或是修改某個原型註冊
* @param prototypeId 原型編號
* @param prototype 原型實例
*/
public synchronized static void setPrototype(String prototypeId , Prototype prototype){
map.put(prototypeId, prototype);
}
/**
* 從原型管理器裏面刪除某個原型註冊
* @param prototypeId 原型編號
*/
public synchronized static void removePrototype(String prototypeId){
map.remove(prototypeId);
}
/**
* 獲取某個原型編號對應的原型實例
* @param prototypeId 原型編號
* @return 原型編號對應的原型實例
* @throws Exception 如果原型編號對應的實例不存在,則拋出異常
*/
public synchronized static Prototype getPrototype(String prototypeId) throws Exception{
Prototype prototype = map.get(prototypeId);
if(prototype == null){
throw new Exception("您希望獲取的原型還沒有註冊或已被銷燬");
}
return prototype;
}
}
客戶端角色:
public class Client {
public static void main(String[]args){
try{
Prototype p1 = new ConcretePrototype1();
PrototypeManager.setPrototype("p1", p1);
//獲取原型來創建對象
Prototype p3 = PrototypeManager.getPrototype("p1").clone();
p3.setName("張三");
System.out.println("第一個實例:" + p3);
//有人動態的切換了實現
Prototype p2 = new ConcretePrototype2();
PrototypeManager.setPrototype("p1", p2);
//重新獲取原型來創建對象
Prototype p4 = PrototypeManager.getPrototype("p1").clone();
p4.setName("李四");
System.out.println("第二個實例:" + p4);
//有人註銷了這個原型
PrototypeManager.removePrototype("p1");
//再次獲取原型來創建對象
Prototype p5 = PrototypeManager.getPrototype("p1").clone();
p5.setName("王五");
System.out.println("第三個實例:" + p5);
}catch(Exception e){
e.printStackTrace();
}
}
}
3、優點和缺點