1.命令模式
package com.designpatter.demo;
/****
* 設計模式
*
* 命令模式
* 命令(Command)模式的定義如下:將一個請求封裝爲一個對象,使發出請求的責任和執行請求的責任分割開。這樣兩者之間通過命令對象進行溝通,
* 這樣方便將命令對象進行儲存、傳遞、調用、增加與管理。
*
* 命令模式的主要優點如下。
* 降低系統的耦合度。命令模式能將調用操作的對象與實現該操作的對象解耦。
* 增加或刪除命令非常方便。採用命令模式增加與刪除命令不會影響其他類,它滿足“開閉原則”,對擴展比較靈活。
* 可以實現宏命令。命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即宏命令。
* 方便實現 Undo 和 Redo 操作。命令模式可以與後面介紹的備忘錄模式結合,實現命令的撤銷與恢復。
*
* 其缺點是:可能產生大量具體命令類。因爲計對每一個具體操作都需要設計一個具體命令類,這將增加系統的複雜性。
*/
public class CommandPatterDemo {
public static void main(String[] args) {
Context command = new Context(new read());
command.execute();
}
public interface Command {
void execute();
}
public static class read implements Command{
@Override
public void execute() {
System.out.println("開始讀取數據");
}
}
public static class copy implements Command{
@Override
public void execute() {
System.out.println("開始複製數據");
}
}
public static class Context{
private Command command;
public Context(Command command) {
this.command = command;
}
public void execute(){
this.command.execute();
}
}
}
2.裝飾模式
package com.designpatter.demo;
/**
* 裝飾模式
*
* 介紹其適用的應用場景,裝飾模式通常在以下幾種情況使用。
* 當需要給一個現有類添加附加職責,而又不能採用生成子類的方法進行擴充時。例如,該類被隱藏或者該類是終極類或者採用繼承方式會產生大量的子類。
* 當需要通過對現有的一組基本功能進行排列組合而產生非常多的功能時,採用繼承關係很難實現,而採用裝飾模式卻很好實現。
* 當對象的功能要求可以動態地添加,也可以再動態地撤銷時。
*
* 裝飾模式在 Java 語言中的最著名的應用莫過於 Java I/O 標準庫的設計了。例如,InputStream 的子類 FilterInputStream,
* OutputStream 的子類 FilterOutputStream,Reader 的子類 BufferedReader 以及 FilterReader,還有 Writer 的子類 BufferedWriter、
* FilterWriter 以及 PrintWriter 等,它們都是抽象裝飾類。
* */
public class DecoratePatternDemo {
public static void main(String[] args) {
SuperPerson superPerson = new SuperPerson(new Person());
superPerson.eat();
}
static class Person{
public void eat(){
System.out.println("喫飯");
}
}
static class SuperPerson{
private Person person;
public SuperPerson(Person person) {
this.person = person;
}
public void eat(){
System.out.println("喝兩口");
this.person.eat();
System.out.println("抽兩根");
}
}
}
3.構建者模式
package com.designpatter.demo;
/**
*
* 建造者(Builder)模式的定義:指將一個複雜對象的構造與它的表示分離,使同樣的構建過程可以創建不同的表示,這樣的設計模式被稱爲建造者模式。
* 它是將一個複雜的對象分解爲多個簡單的對象,然後一步一步構建而成。它將變與不變相分離,即產品的組成部分是不變的,但每一部分是可以靈活選擇的。
*
* 該模式的主要優點如下:
* 各個具體的建造者相互獨立,有利於系統的擴展。
* 客戶端不必知道產品內部組成的細節,便於控制細節風險。
*
* 其缺點如下:
* 產品的組成部分必須相同,這限制了其使用範圍。
* 如果產品的內部變化複雜,該模式會增加很多的建造者類。
*
* 建造者(Builder)模式和工廠模式的關注點不同:建造者模式注重零部件的組裝過程,而工廠方法模式更注重零部件的創建過程,但兩者可以結合使用。
* */
public class WithoutPatternDemo {
public static void main(String[] args) {
Student student = new ConCreateStudent()
.setSpeak1("test1")
.setSpeak2("test2")
.setSpeak3("test3")
.build();
System.out.println(student);
}
public static class Student{
private String speak1;
private String speak2;
private String speak3;
public void setSpeak1(String speak1) {
this.speak1 = speak1;
}
public void setSpeak2(String speak2) {
this.speak2 = speak2;
}
public void setSpeak3(String speak3) {
this.speak3 = speak3;
}
public String getSpeak1() {
return speak1;
}
public String getSpeak2() {
return speak2;
}
public String getSpeak3() {
return speak3;
}
@Override
public String toString() {
return "Student{" +
"speak1='" + speak1 + '\'' +
", speak2='" + speak2 + '\'' +
", speak3='" + speak3 + '\'' +
'}';
}
}
public interface Buidler{
Buidler setSpeak1(String speak1);
Buidler setSpeak2(String speak2);
Buidler setSpeak3(String speak3);
Student build();
}
public static class ConCreateStudent implements Buidler {
Student student = new Student();
@Override
public Buidler setSpeak1(String speak1) {
System.out.println("複雜的邏輯判斷");
student.setSpeak1(speak1);
return this;
}
@Override
public Buidler setSpeak2(String speak2) {
System.out.println("複雜的邏輯判斷");
student.setSpeak2(speak2);
return this;
}
@Override
public Buidler setSpeak3(String speak3) {
System.out.println("複雜的邏輯判斷");
student.setSpeak3(speak3);
return this;
}
@Override
public Student build() {
return student;
}
}
}
4.組合模式
package com.designpatter.demo;
import java.util.ArrayList;
import java.util.List;
/**
* 組合模式的主要優點有:
* 組合模式使得客戶端代碼可以一致地處理單個對象和組合對象,無須關心自己處理的是單個對象,還是組合對象,這簡化了客戶端代碼;
* 更容易在組合體內加入新的對象,客戶端不會因爲加入了新的對象而更改源代碼,滿足“開閉原則”;
*
* 其主要缺點是:
* 設計較複雜,客戶端需要花更多時間理清類之間的層次關係;
* 不容易限制容器中的構件;
* 不容易用繼承的方法來增加構件的新功能;
* */
public class WhihoutPatternDemo {
public static void main(String[] args) {
Department coreDep = new Department("主部門");
Department subDep1 = new Department("子部門1");
Department subDep2 = new Department("子部門2");
Department leafDep1 = new Department("葉子部門1");
Department leafDep2 = new Department("葉子部門2");
Department leafDep3 = new Department("葉子部門3");
subDep1.child.add(leafDep1);
subDep2.child.add(leafDep2);
subDep2.child.add(leafDep3);
coreDep.child.add(subDep1);
coreDep.child.add(subDep2);
coreDep.remove();
}
static class Department{
private String name;
private List<Department> child = new ArrayList<Department>();
public Department(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<Department> getChild() {
return child;
}
public void setName(String name) {
this.name = name;
}
public void setChild(List<Department> child) {
this.child = child;
}
void remove(){
if(this.child.size() > 0){
for(Department department:this.child){
department.remove();
}
}
System.out.println("刪除"+name);
}
}
}
#程序開發遵守原則
1.開閉原則
定義:
當應用的需求改變時,在不修改軟件實體的源代碼或者二進制代碼的前提下,可以擴展模塊的功能,使其滿足新的需求。
作用:
開閉原則是面向對象程序設計的終極目標,它使軟件實體擁有一定的適應性和靈活性的同時具備穩定性和延續性。具體來說,其作用如下。
- 對軟件測試的影響軟件遵守開閉原則的話,軟件測試時只需要對擴展的代碼進行測試就可以了,因爲原有的測試代碼仍然能夠正常運行。
- 可以提高代碼的可複用性粒度越小,被複用的可能性就越大;在面向對象的程序設計中,根據原子和抽象編程可以提高代碼的可複用性。
- 可以提高軟件的可維護性遵守開閉原則的軟件,其穩定性高和延續性強,從而易於擴展和維護。
實現:
可以通過“抽象約束、封裝變化”來實現開閉原則,即通過接口或者抽象類爲軟件實體定義一個相對穩定的抽象層,而將相同的可變因素封裝在相同的具體實現類中。
2.里氏替換原則
定義:
里氏替換原則主要闡述了有關繼承的一些原則,也就是什麼時候應該使用繼承,什麼時候不應該使用繼承,以及其中蘊含的原理。里氏替換原是繼承複用的基礎,它反映了基類與子類之間的關係,是對開閉原則的補充,是對實現抽象化的具體步驟的規範。
作用:
- 里氏替換原則是實現開閉原則的重要方式之一。
- 它克服了繼承中重寫父類造成的可複用性變差的缺點。
- 它是動作正確性的保證。即類的擴展不會給已有的系統引入新的錯誤,降低了代碼出錯的可能性。
實現:
- 里氏替換原則通俗來講就是:子類可以擴展父類的功能,但不能改變父類原有的功能。也就是說:子類繼承父類時,除添加新的方法完成新增功能外,儘量不要重寫父類的方法。
- 如果通過重寫父類的方法來完成新的功能,這樣寫起來雖然簡單,但是整個繼承體系的可複用性會比較差,特別是運用多態比較頻繁時,程序運行出錯的概率會非常大。
- 如果程序違背了里氏替換原則,則繼承類的對象在基類出現的地方會出現運行錯誤。這時其修正方法是:取消原來的繼承關係,重新設計它們之間的關係。
3.依賴倒置原則
定義:
依賴倒置原則的原始定義爲:高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象(High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions)。其核心思想是:要面向接口編程,不要面向實現編程。
依賴倒置原則是實現開閉原則的重要途徑之一,它降低了客戶與實現模塊之間的耦合。
作用:
- 依賴倒置原則可以降低類間的耦合性。
- 依賴倒置原則可以提高系統的穩定性。
- 依賴倒置原則可以減少並行開發引起的風險。
- 依賴倒置原則可以提高代碼的可讀性和可維護性。
實現:
- 每個類儘量提供接口或抽象類,或者兩者都具備。
- 變量的聲明類型儘量是接口或者是抽象類。
- 任何類都不應該從具體類派生。
- 使用繼承時儘量遵循里氏替換原則。
4.單一職責原則
定義:
一個職責的變化可能會削弱或者抑制這個類實現其他職責的能力;
當客戶端需要該對象的某一個職責時,不得不將其他不需要的職責全都包含進來,從而造成冗餘代碼或代碼的浪費。
作用:
- 降低類的複雜度。一個類只負責一項職責,其邏輯肯定要比負責多項職責簡單得多。
- 提高類的可讀性。複雜性降低,自然其可讀性會提高。
- 提高系統的可維護性。可讀性提高,那自然更容易維護了。
- 變更引起的風險降低。變更是必然的,如果單一職責原則遵守得好,當修改一個功能時,可以顯著降低對其 他功能的影響
實現:
單一職責原則是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,再封裝到不同的類或模塊中。而發現類的多重職責需要設計人員具有較強的分析設計能力和相關重構經驗。下面以大學學生工作管理程序爲例介紹單一職責原則的應用。
5.接口隔離原則
定義:
- 單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離。
- 單一職責原則主要是約束類,它針對的是程序中的實現和細節;接口隔離原則主要約束接口,主要針對抽象和程序整體框架的構建。
作用:
- 將臃腫龐大的接口分解爲多個粒度小的接口,可以預防外來變更的擴散,提高系統的靈活性和可維護性。
- 接口隔離提高了系統的內聚性,減少了對外交互,降低了系統的耦合性。
- 如果接口的粒度大小定義合理,能夠保證系統的穩定性;但是,如果定義過小,則會造成接口數量過多,使設計複雜化;如果定義太大,靈活性降低,無法提供定製服務,給整體項目帶來無法預料的風險。
- 使用多個專門的接口還能夠體現對象的層次,因爲可以通過接口的繼承,實現對總接口的定義。
- 能減少項目工程中的代碼冗餘。過大的大接口裏面通常放置許多不用的方法,當實現這個接口的時候,被迫設計冗餘的代碼。
實現:
- 接口儘量小,但是要有限度。一個接口只服務於一個子模塊或業務邏輯。
- 爲依賴接口的類定製服務。只提供調用者需要的方法,屏蔽不需要的方法。
- 瞭解環境,拒絕盲從。每個項目或產品都有選定的環境因素,環境不同,接口拆分的標準就不同深入瞭解業務邏輯。
- 提高內聚,減少對外交互。使接口用最少的方法去完成最多的事情。
6.迪米特原則
定義:
如果兩個軟件實體無須直接通信,那麼就不應當發生直接的相互調用,可以通過第三方轉發該調用。其目的是降低類之間的耦合度,提高模塊的相對獨立性。
作用:
- 降低了類之間的耦合度,提高了模塊的相對獨立性。
- 由於親合度降低,從而提高了類的可複用率和系統的擴展性。
實現:
- 在類的劃分上,應該創建弱耦合的類。類與類之間的耦合越弱,就越有利於實現可複用的目標。
- 在類的結構設計上,儘量降低類成員的訪問權限。
- 在類的設計上,優先考慮將一個類設置成不變類。
- 在對其他類的引用上,將引用其他對象的次數降到最低。
- 不暴露類的屬性成員,而應該提供相應的訪問器(set 和 get 方法)。
- 謹慎使用序列化(Serializable)功能。
7.合成複用原則
定義:
- 它要求在軟件複用時,要儘量先使用組合或者聚合等關聯關係來實現,其次才考慮使用繼承關係來實現。
- 如果要使用繼承關係,則必須嚴格遵循里氏替換原則。合成複用原則同里氏替換原則相輔相成的,兩者都是開閉原則的具體實現規範。
作用:
- 它維持了類的封裝性。因爲成分對象的內部細節是新對象看不見的,所以這種複用又稱爲“黑箱”複用。
- 新舊類之間的耦合度低。這種複用所需的依賴較少,新對象存取成分對象的唯一方法是通過成分對象的接口。
- 複用的靈活性高。這種複用可以在運行時動態進行,新對象可以動態地引用與成分對象類型相同的對象。
實現:
合成複用原則是通過將已有的對象納入新對象中,作爲新對象的成員對象來實現的,新對象可以調用已有對象的功能,從而達到複用
- 單例(Singleton)模式:某個類只能生成一個實例,該類提供了一個全局訪問點供外部獲取該實例,其拓展是有限多例模式。
- 原型(Prototype)模式:將一個對象作爲原型,通過對其進行復制而克隆出多個和原型類似的新實例。
- 工廠方法(Factory Method)模式:定義一個用於創建產品的接口,由子類決定生產什麼產品。
- 抽象工廠(AbstractFactory)模式:提供一個創建產品族的接口,其每個子類可以生產一系列相關的產品。
- 建造者(Builder)模式:將一個複雜對象分解成多個相對簡單的部分,然後根據不同需要分別創建它們,最後構建成該複雜對象。
- 代理(Proxy)模式:爲某對象提供一種代理以控制對該對象的訪問。即客戶端通過代理間接地訪問該對象,從而限制、增強或修改該對象的一些特性。
- 適配器(Adapter)模式:將一個類的接口轉換成客戶希望的另外一個接口,使得原本由於接口不兼容而不能一起工作的那些類能一起工作。
- 橋接(Bridge)模式:將抽象與實現分離,使它們可以獨立變化。它是用組合關係代替繼承關係來實現,從而降低了抽象和實現這兩個可變維度的耦合度。
- 裝飾(Decorator)模式:動態的給對象增加一些職責,即增加其額外的功能。
- 外觀(Facade)模式:爲多個複雜的子系統提供一個一致的接口,使這些子系統更加容易被訪問。
- 享元(Flyweight)模式:運用共享技術來有效地支持大量細粒度對象的複用。
- 組合(Composite)模式:將對象組合成樹狀層次結構,使用戶對單個對象和組合對象具有一致的訪問性。
- 模板方法(TemplateMethod)模式:定義一個操作中的算法骨架,而將算法的一些步驟延遲到子類中,使得子類可以不改變該算法結構的情況下重定義該算法的某些特定步驟。
- 策略(Strategy)模式:定義了一系列算法,並將每個算法封裝起來,使它們可以相互替換,且算法的改變不會影響使用算法的客戶。
- 命令(Command)模式:將一個請求封裝爲一個對象,使發出請求的責任和執行請求的責任分割開。
- 職責鏈(Chain of Responsibility)模式:把請求從鏈中的一個對象傳到下一個對象,直到請求被響應爲止。通過這種方式去除對象之間的耦合。
- 狀態(State)模式:允許一個對象在其內部狀態發生改變時改變其行爲能力。
- 觀察者(Observer)模式:多個對象間存在一對多關係,當一個對象發生改變時,把這種改變通知給其他多個對象,從而影響其他對象的行爲。
- 中介者(Mediator)模式:定義一箇中介對象來簡化原有對象之間的交互關係,降低系統中對象間的耦合度,使原有對象之間不必相互瞭解。
- 迭代器(Iterator)模式:提供一種方法來順序訪問聚合對象中的一系列數據,而不暴露聚合對象的內部表示。
- 訪問者(Visitor)模式:在不改變集合元素的前提下,爲一個集合中的每個元素提供多種訪問方式,即每個元素有多個訪問者對象訪問。
- 備忘錄(Memento)模式:在不破壞封裝性的前提下,獲取並保存一個對象的內部狀態,以便以後恢復它。
- 解釋器(Interpreter)模式:提供如何定義語言的文法,以及對語言句子的解釋方法,即解釋器。