Java23種設計模式/GOF設計模式--建立型模式
目錄
設計模式
維基百科把設計模式分為了設計模式和軟體設計模式兩個WIKI,其中前者指的是GOF的設計模式,GOF設計模式針對的是C++和smalltalk等面向物件的高階語言,而軟體設計模式是更大的概念,包含了更多模式,實際上後者中很多模式在Java也有廣泛的應用,這裡只談GOF的23種設計模式。
面向物件
面向物件是對世界的抽象,一切皆物件,這種高階語言本身就是一種最頂層的設計。理論上講,基於這種語言開發系統時搞清需求、設計業務流程圖、設計業務實體,然後把這些實體轉化成類,一個個用程式碼實現系統就做完了,不需要什麼設計模式,早期一些小系統也就是這麼做的。
然後事實卻不盡如人意,原因是系統越來越複雜,需要支撐的功能越來越多,對易維護、易拓展的要求越來越高。應用不再單單跟自己打交道,應用之間往往需要相互打交道,構成龐大的系統,外部的不確定性對應用構成挑戰。系統的硬體資源要求軟體與之配合。
越來越多的因素要求軟體要有一定的設計,最好能應付所有的問題。在越來越多的實踐上,人們總結出些經驗,這些經驗有很多是用失敗換來的。
可以說六大設計原則是設計最濃縮的精華,也是對設計模式的要求。在遵循設計原則的基礎上,會不自知的使用設計模式,哪怕你還不瞭解設計模式。
設計模式的六大原則
1、開閉原則(Open Close Principle)
開閉原則就是說對擴充套件開放,對修改關閉
在程式需要進行拓展的時候,不能去修改原有的程式碼,實現一個熱插拔的效果。所以一句話概括就是:為了使程式的擴充套件性好,易於維護和升級。想要達到這樣的效果,我們需要使用介面和抽象類,後面的具體設計中我們會提到這點。
2、里氏代換原則(Liskov Substitution Principle)
里氏代換原則(Liskov Substitution Principle LSP)面向物件設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。
LSP是繼承複用的基石,只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行為。
里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科
3、依賴倒轉原則(Dependence Inversion Principle)
這個是開閉原則的基礎,具體內容:真對介面程式設計,依賴於抽象而不依賴於具體。
4、介面隔離原則(Interface Segregation Principle)
這個原則的意思是:使用多個隔離的介面,比使用單個介面要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟體的設計思想,從大型軟體架構出發,為了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。
5、迪米特法則(最少知道原則)(Demeter Principle)
為什麼叫最少知道原則,就是說:一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。
6、合成複用原則(Composite Reuse Principle)
原則是儘量使用合成/聚合的方式,而不是使用繼承。
建立型模式
結構型模式
行為型模式
建立型模式
1、工廠模式
-
工廠方法(Factory method pattern)
工廠方法模式的實質是“定義一個建立物件的介面,但讓實現這個介面的類來決定例項化哪個類。工廠方法讓類的例項化推遲到子類中進行。”
在面向物件程式設計中,工廠通常是一個用來建立其他物件的物件。工廠是構造方法的抽象,用來實現不同的分配方案。工廠物件通常包含一個或多個方法,用來建立這個工廠所能建立的各種型別的物件。這些方法可能接收引數,用來指定物件建立的方式,最後返回建立的物件。
有時,特定型別物件的控制過程比簡單地建立一個物件更復雜。在這種情況下,工廠物件就派上用場了。工廠物件可能會動態地建立產品物件的類,或者從物件池中返回一個物件,或者對所建立的物件進行復雜的配置,或者應用其他的操作。
UML用例圖
程式碼舉例
例如,有一個Button
類表示按鈕,另有它的兩個子類WinButton
和MacButton
分別代表Windows和Mac風格的按鈕,那麼這幾個類和用於建立它們的工廠類在Java中可以如下實現(在此省略所有類和方法的可見性設定):
//幾個Button類
class Button{/* ...*/}
class WinButton extends Button{/* ...*/}
class MacButton extends Button{/* ...*/}
//他們的工廠類
interface ButtonFactory{
abstract Button createButton();
}
class WinButtonFactory implements ButtonFactory{
Button createButton(){
return new WinButton();
}
}
class MacButtonFactory implements ButtonFactory{
Button createButton(){
return new MacButton();
}
}
-
簡單工廠
普通的工廠方法模式通常伴隨著物件的具體型別與工廠具體型別的一一對應,客戶端程式碼根據需要選擇合適的具體型別工廠使用。
然而,這種選擇可能包含複雜的邏輯。這時,可以建立一個單一的工廠類,用以包含這種選擇邏輯,根據引數的不同選擇實現不同的具體物件。這個工廠類不需要由每個具體產品實現一個自己的具體的工廠類,所以可以將工廠方法設定為靜態方法。 而且,工廠方法封裝了物件的建立過程。
如果建立過程非常複雜(比如依賴於配置檔案或使用者輸入),工廠方法就非常有用了。 比如,一個程式要讀取影象檔案。程式支援多種影象格式,每種格式都有一個對應的ImageReader
類用來讀取影象。程式每次讀取影象時,需要基於檔案資訊建立合適型別的ImageReader
。這個選擇邏輯可以包裝在一個簡單工廠中:
程式碼舉例
public class ImageReaderFactory {
public static ImageReader imageReaderFactoryMethod(InputStream is) {
ImageReader product = null;
int imageType = determineImageType(is);
switch (imageType) {
case ImageReaderFactory.GIF:
product = new GifReader(is);
case ImageReaderFactory.JPEG:
product = new JpegReader(is);
//...
}
return product;
}
}
2、抽象工廠(Abstract factory pattern)
抽象工廠是“工廠方法”的進一步升級,每個工廠生產一組產品,或者說一個產品的一組元件。
形式上工廠方法是抽象工廠的只有單一產品的特例,但概念上抽象工廠與工廠方法不同,抽象解決的是“同一種產品”的各個元件的變化性,這種變化往往是業務的需求,進而演化出不同的系列產品。參考thinkpad的T系列,E系列,他們擁有相同的鍵盤(thinkpad的鍵盤真的太好使了),但是螢幕、A面材質可能不同。
UML用例圖
程式碼舉例
//產品介面
public interface Button {}
public interface Border {}
// 實現抽象類
public class MacButton implements Button {}
public class MacBorder implements Border {}
public class WinButton implements Button {}
public class WinBorder implements Border {}
// 接著實現工廠
public class MacFactory {
public static Button createButton() {
return new MacButton();
}
public static Border createBorder() {
return new MacBorder();
}
}
public class WinFactory {
public static Button createButton() {
return new WinButton();
}
public static Border createBorder() {
return new WinBorder();
}
}
3、單例模式(Singleton pattern)
單例模式,也叫單子模式,是一種常用的軟體設計模式。
在應用這個模式時,單例物件的類必須保證只有一個例項存在。許多時候整個系統只需要擁有一個的全域性物件,這樣有利於我們協調系統整體的行為。比如在某個伺服器程式中,該伺服器的配置資訊存放在一個檔案中,這些配置資料由一個單例物件統一讀取,然後服務程序中的其他物件再通過這個單例物件獲取這些配置資訊。這種方式簡化了在複雜環境下的配置管理。
實現單例模式的思路是:一個類能返回物件一個引用(永遠是同一個)和一個獲得該例項的方法(必須是靜態方法,通常使用getInstance這個名稱);當我們呼叫這個方法時,如果類持有的引用不為空就返回這個引用,如果類保持的引用為空就建立該類的例項並將例項的引用賦予該類保持的引用;同時我們還將該類的建構函式定義為私有方法,這樣其他處的程式碼就無法通過呼叫該類的建構函式來例項化該類的物件,只有通過該類提供的靜態方法來得到該類的唯一例項。
單例模式在多執行緒的應用場合下必須小心使用。如果當唯一例項尚未建立時,有兩個執行緒同時呼叫建立方法,那麼它們同時沒有檢測到唯一例項的存在,從而同時各自建立了一個例項,這樣就有兩個例項被構造出來,從而違反了單例模式中例項唯一的原則。 解決這個問題的辦法是為指示類是否已經例項化的變數提供一個互斥鎖(雖然這樣會降低效率)。
程式碼舉例
通常單例模式在Java中,有兩種構建方式:
- 懶漢方式。指全域性的單例例項在第一次被使用時構建。
- 餓漢方式。指全域性的單例例項在類裝載時構建。(static程式碼塊中)
單例模式(餓漢模式)應用的例子如下述程式碼所示:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
// Private constructor suppresses
private Singleton() {}
// default public constructor
public static Singleton getInstance() {
return INSTANCE;
}
}
單例模式(懶漢模式)應用的例子如下述程式碼所示 (此種方法只能用在JDK5及以後版本(注意 INSTANCE 被宣告為 volatile),之前的版本使用“雙重檢查鎖”會發生非預期行為:
public class Singleton {
private static volatile Singleton INSTANCE = null;
// Private constructor suppresses
// default public constructor
private Singleton() {}
//thread safe and performance promote
public static Singleton getInstance() {
if(INSTANCE == null){
synchronized(Singleton.class){
//when more than two threads run into the first null check same time, to avoid instanced more than one time, it needs to be checked again.
if(INSTANCE == null){
INSTANCE = new Singleton();
}
}
}
return INSTANCE;
}
}
4、建造者模式(Builder Pattern)
它可以將複雜物件的建造過程抽象出來(抽象類別),使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件。
在以下情況使用生成器模式:
- 當建立複雜物件的演算法應該獨立於該物件的組成部分以及它們的裝配方式時;
- 當構造過程必須允許被構造的物件有不同的表示時。
參與者
- Builder
為建立一個Product物件的各個部件指定抽象介面。
- ConcreteBuilder
實現Builder的介面以構造和裝配該產品的各個部件。
定義並明確它所建立的表示。
提供一個檢索產品的介面
- Director
構造一個使用Builder介面的物件。
- Product
表示被構造的複雜物件。ConcreateBuilder建立該產品的內部表示並定義它的裝配過程。
包含定義組成部件的類,包括將這些部件裝配成最終產品的介面。
UML用例圖
程式碼舉例
/**
* Represents the product created by the builder.
*/
class Car {
private int wheels;
private String color;
public Car() {
}
public String getColor() {
return color;
}
public void setColor(final String color) {
this.color = color;
}
public int getWheels() {
return wheels;
}
public void setWheels(final int wheels) {
this.wheels = wheels;
}
@Override
public String toString() {
return "Car [wheels = " + wheels + ", color = " + color + "]";
}
}
/**
* The builder abstraction.
*/
interface CarBuilder {
Car build();
CarBuilder setColor(final String color);
CarBuilder setWheels(final int wheels);
}
class CarBuilderImpl implements CarBuilder {
private Car car;
public CarBuilderImpl() {
car = new Car();
}
@Override
public Car build() {
return car;
}
@Override
public CarBuilder setColor(final String color) {
car.setColor(color);
return this;
}
@Override
public CarBuilder setWheels(final int wheels) {
car.setWheels(wheels);
return this;
}
}
public class CarBuildDirector {
private CarBuilder builder;
public CarBuildDirector(final CarBuilder builder) {
this.builder = builder;
}
public Car construct() {
return builder.setWheels(4)
.setColor("Red")
.build();
}
public static void main(final String[] arguments) {
final CarBuilder builder = new CarBuilderImpl();
final CarBuildDirector carBuildDirector = new CarBuildDirector(builder);
System.out.println(carBuildDirector.construct());
}
}
5、原型模式(Prototype pattern)
原型模式是建立型模式的一種,其特點在於通過“複製”一個已經存在的例項來返回新的例項,而不是新建例項。被複制的例項就是我們所稱的“原型”,這個原型是可定製的。
原型模式多用於建立複雜的或者耗時的例項,因為這種情況下,複製一個已經存在的例項使程式執行更高效;或者建立值相等,只是命名不一樣的同類資料。
UML
程式碼舉例(淺複製)(更多是使用序列化和反序列化實現深複製,這個要看需求)
// Prototype pattern
public abstract class Prototype implements Cloneable {
public Prototype clone() throws CloneNotSupportedException{
return (Prototype) super.clone();
}
}
public class ConcretePrototype1 extends Prototype {
@Override
public Prototype clone() throws CloneNotSupportedException {
return (ConcretePrototype1)super.clone();
}
}
public class ConcretePrototype2 extends Prototype {
@Override
public Prototype clone() throws CloneNotSupportedException {
return (ConcretePrototype2)super.clone();
}
}