23種 設計模式 java 例項程式碼
23種設計模式
文章目錄
簡介:
1)、設計模式是程式設計師在面對同類軟體工程設計問題所總結出來的有用的經驗,模式不是程式碼,而是某類問題 的通用解決方案,設計模式(Designpatterm)代表了最佳的實踐。這些解決方案是眾多軟體開發人員經過 相當長的一段時間的試驗和錯誤總結出來的。
2)、設計模式的本 質提高軟體的維護性,通用性和擴充套件性,並降低軟體的複雜度。
3) 、<<設計模式>> 是經典的書,作者是Erich Gamma、Richard Helm、Ralph Johnson和John Vissides Design (俗稱“四人組 GOF”)
-
為什麼學
學設計模式是為了對軟體設計中普遍存在的(反覆出現)的各種問題,提出解決方案。
為了解決某一類問題
編寫軟體過程中,程式設計師面臨來自耦合性,內聚性以及可維護性,可擴寬性,重用性,靈活性的挑戰,設計模式是為了讓程式具有更好的
- 程式碼重用性
- 可讀性
- 可擴充套件性 (當需要增加新的功能,非常方便)
- 可靠性 (增加新的功能,對原來的功能沒有影響)
- 使程式呈現高內聚,低耦合的特性
-
有什麼用
當一個專案開發完後,如果有客戶提出增新功能,使用設計模式會有很好的擴充套件性
原來程式設計師離職,接手專案比較容易
-
怎麼用
1)、第1層:剛開始學程式設計不久,聽說過什麼是設計模式
2)、第2層:有很長時間的程式設計經驗,自己寫了很多程式碼,其中用到了設計模式,但.是自己卻不知道
3)、第3層:學習過了設計模式,發現自己已經在使用了,並且發現了一些新的模式挺好用的
4)、第4層:閱讀了很多別人寫的原始碼和框架,在其中看到別人設計模式,並且能夠領會設計模式的精妙和 帶來的好處。
5)、第5層:程式碼寫著寫著,自己都沒有意識到使用了設計模式,並且熟練的寫了出來。
一、設計模式的6/7 大原則:
●設計原則核心思想
1)、找出應用中可能需要變化之處,把它們獨立出來,不要和那些不需要變化的程式碼混在–起。
2)、針對介面程式設計,而不是針對實現程式設計。
3)、為了互動物件之間的鬆耦合設計而努力
背過:
1、開閉原則(Open Close Principle)
對擴充套件開放(對提供方),對修改關閉(對使用方)。
2、里氏替換原則(Liskov Substitution Principle)
只有當衍生類可以替換掉基類,軟體單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在 基類的基礎上增加新的行為。
3、依賴倒轉原則(Dependence Inversion Principle)
這個是開閉原則的基礎,對介面程式設計,依賴於抽象而不依賴於具體。
4、介面隔離原則(Interface Segregation Principle)
使用多個隔離的介面來降低耦合度。一個類對另一個類的依賴應該建立在最小介面上。
5、迪米特法則(最少知道原則) (Demeter Principle)
一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模組相對獨立。
6、合成複用原則(Composite Reuse Principle)
原則是儘量使用合成/聚合的方式,而不是使用繼承。繼承實際上破壞了類的封裝性,超類的方法可能會 被子類修改。
-
單一職責原則
一個類應該只負責一項職責。如類A負責兩個不同職責:職責1,職責2。當職責1需求變更而改變A時,可能造成職責2執行錯誤,所以需要將類A的粒度分解為A1這個類和A2這個類
- 降低類的複雜度,一個類只負責一項職責。
- 提高類的可讀性,可維護性
- 降低變更引起的風險
- 通常情況下,我們應當遵守單一職責原則,只有邏輯足夠簡單,才可以在程式碼違反單一職責原則;只有類中的方法數量足夠少,才可以在方法級別保持單一職責原則
-
介面隔離原則
分割介面
-
客戶端不應該依賴它不需要的介面
-
一個類對另一個類的依賴應該建立在最小介面上。
-
-
依賴倒轉原則
- 依賴倒轉的中心思想是面向介面程式設計。
- 依賴的是抽象不應該依賴細節(具體),細節應該依賴抽象。
- 使用介面。抽象類的目的是練好規範,而不涉及任何具體的操作,把展現細節的任務交給實現類去完成
- 依賴倒轉原則的注意事項和細節。
- 低沉模組儘量都要有抽象類或介面,或者兩者都有程式穩定性比較好。
- 變數的宣告型別儘量是抽象類或介面,這樣我們的變數引用和實際物件間就存在一個緩衝層,易於程式擴充套件和優化。
- 繼承時遵循里氏替換原則。
-
里氏替換原則
- 所有引用基類的地方必須能透明的使用期間之內的物件。
- 在使用繼承時,遵循里氏替換原則,在子類中儘量不要重寫父類的方法。
- 里氏替換原則告訴我們,繼承實際上讓兩個類耦合性增強了,**在適當的情況下,可以通過聚合,組合(注入),依賴(介面替換)。來解決問題。**讓兩個類繼承一個更加基礎的類。
-
開閉原則ocp
- 開閉原則(Open Closed Principle) 是程式設計中最基礎最重要的設計原則。
- 一個軟體實體如類,模組和函式應該對擴充套件開放(對提供方),對修改關閉(對使用方)。用抽象構建框架,用實現擴充套件細節。
- 當軟體需要變化時,儘量通過擴充套件軟體實體的行為來實現變化,而不是通過修改已有的程式碼來實現變化。
- 程式設計中遵循其他原則,以及使用設計模式的目的就是遵循開閉原則。
-
迪米特法則
- 一個物件應該對其他物件保持最少的瞭解。
- 類與類關係越密切。耦合度越大。
- 迪米特法則(Demeter Principle)又叫最少知道法則,及一個類對自己依賴的那。知道的越少越好,也就是說對於被依賴人類,不管多麼複雜,都儘量將邏輯封裝在類的內部。對於除了提供public方法不對外洩露任何資訊。
- 迪米特法則。還有個更簡單的定義,只與直接的朋友通訊。出現成員變數方法引數,方法返回值中的那稱為直接的朋友。而出現區域性變數中,那我是直接的朋友。陌生的類最好不要以區域性變數的形式出現在類的內部。
- 迪米特法則注意事項和細節
- 迪米特法則的核心是降低類之間的耦合。
- 但是注意由於每個類都減少了不必要的依賴。因為迪米特法則只是要求降低,類之間物件間耦合關係並不是要求完全沒有依賴關係。
-
合成複用原則
原則是儘量使用合成/聚合的方式,而不是使用繼承。
1)、UML類圖定義
-
UML–Unified modeling language UML(統一建模語言),是一種用於軟體系統分析和設計的語言工具,它用於幫助軟體開發人員進行思考和記錄思路的結果
-
UML本身是一套符號的規定,就像數學符號和化學符號-樣,這些符號用於描述軟體模型中的各個元素和他們之間的關係,比如類、介面、實現、泛化、依賴、組合、聚合等。
-
使用UML來建模,常用的工具有RationalRose,也可以使用一些外掛來建模。
Common 常用
Note 對你的UML註釋
Class 表示類,可以新增屬性和方法。
Interface 表示介面。
Relation
Dependency 依賴。
Association 關聯。
Generalization 表示泛化(繼承。)
Realization 表示實現。
Aggregation 聚合。
Composite 組合。耦合性要比聚合強。1)、用例圖(use case)
2)、靜態結構圖: 類圖、物件圖、包圖、元件圖、部署圖.
3)、動態行為圖: 互動圖(時序圖與協作圖)、狀態圖、活動圖類圖: 是描述類與類之間的關係的,是UML圖中最核心的
2)、UML類圖詳解
1)、用於描述系統中的類(物件)本身的組成和類(物件)之間的各種靜態關係。
2)、類之間的關係: 依賴、泛化(繼承)、實現、關聯、聚合與組合。
1. 名稱詳解
依賴(Dependency)
如果有以下方式、我們就稱之為依賴
- 、類中用到了對方
2)、如果是類的成員屬性
3)、如果是方法的返回型別
4)、是方法接收的引數型別
5)、方法中使用到
只要是在類中用到了對方, 那麼他們之間就存在依賴關係。如果沒有對方,連編繹都通過不了。
public class PersonServiceBean {
private PersonDao personDao;/類
public void save(Person person){}
public IDCard getIDCard(Integer personid){}
public void modifyO{
Depatment department= new Depatment{};
}
}
public class PersonDao{}
public class IDCard{}
public class Person{}
public class Department{}
泛化(Generalization)
泛化關係實際上就是繼承關係,他是依賴關係的特例
1)、泛化關係實際上就是繼承關係
2)、如果A類繼承了B類, 我們就說A和B存在泛化關係
public abstract class DaoSupport{
public void save(Object entity){}
public void delete(Object id){ }
}
public class PersonServiceBean extends Daosupport{}
實現(Realization)
實現關係實際上就是A類實現B類,他是依賴關係的特例
public interface PersonService {
public void delete(Interger id){}
}
public class PersonServiceBean implements PersonService {
public void delete(Interger id){}
}
關聯(Association)
關聯關係實際上就是類與類之間的聯絡,他是依賴關係的特例
關聯具有導航性: 即雙向關係或單向關係
單向一對一關係 我用到你。
public class Person {
private IDCard card;
}
public class IDCard{}
雙向- -對一關係 我用到你,你用到我。
public class Person {
private IDCard card;
}
public class IDCard{
private Person person{ }
}
聚合(Aggregation)
- 聚合關係(Aggregation) 表示的是整體和部分的關係,整體與部分可以分開。聚
合關係是關聯關係的特例,所以他具有關聯的導航性與多重性。 - 如: -臺電腦由鍵盤(keyboard)、顯示器(monitor),滑鼠等組成;組成電腦的各個
配件是可以從電腦.上分離出來的,使用帶空心菱形的實線來表示:
public class Computer {
private Mouse mouse; //滑鼠可以和computer分離
private Moniter moniter; //顯 示器可以和Computer分離
public void setMouse(Mouse mouse) {
this.mouse = mouse ;
}
public void setMoniter(Moniter moniter) {
this.moniter = moniter;
}
}
沒有這兩個類(Mouse and Moniter)對Computer類的建立沒有影響。
組合(Composite)
- 組合關係:也是整體與部分的關係,但是整體與部分不可以分開。實心菱形指向使用的類
只要Person建立了Head就建立了,所以不能分離。人可以沒有身份證,但是不能沒有頭。
public class Person{
private IDCard card;
private Head head = new Head();
public class IDCard{}
public class Head{}
}
- 但是如果在程式中Person實體中定義了對IDCard進行級聯刪除,即刪除Person時連同IDCard一起刪除,那麼IDCard和Person就是組合關係了
二、設計模式分為三種類型23種
對於新手時間比較緊迫的,先追一下這幾個,可以去招聘網站上看一下,需求的設計模式
- 建立型模式: 單例模式、建造者模式、工廠模式、
- 結構型模式: 介面卡模式、代理模式
- 行為型模式: 模板方法模式、策略模式
設計模式分為三種類型,共23種
-
建立型模式:單例模式、抽象工廠模式、原型模式、建造者模式、工模式。
-
結構型模式:介面卡模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
-
行為型模式:模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、直譯器模式(Interpreter模式) 、狀態模式、策略模式、職責鏈模式(責任鏈模式)。
注意:不同的書籍.上對分類和名稱略有差別
1)、建立型模式
1 建立型模式: 單例模式、抽象工廠模式、原型模式、建造者模式、工廠模式。
學完一個類別的就花時間想想這個類別都有啥,每個模式是幹啥的
1. 單例模式、
所謂類的單例設計模式,就是採取一定的方法保證在整個的軟體系統中,對某個類只能存在一個物件例項,並且該類只提供一個取得其物件例項的方法(靜態的)。
- 餓漢式(靜態常量)
2) 餓漢式(靜態程式碼塊)
3) 懶漢式(執行緒不安全)–不推薦
4) 懶漢式(執行緒安全,同步方法)–不推薦
5)懶漢式(執行緒安全,同步程式碼塊)
6) 雙重檢查
7) 靜態內部類
8) 列舉
- 單例模式注意事項和細節說明
1)、單例模式保證了系統記憶體中該類只存在一一個物件,節省了系統資源,對於一些需要頻繁建立銷燬的 物件,使用單例模式可以提高系統性能
2)、當想例項化一個單例類的時候,必須要記住使用相應的獲取物件的方法,而不是使用new
3)、單例模式使用的場景:需要頻繁的進行建立和銷燬的物件、建立物件時耗時過多或耗費資源過多(即:重 量級物件),但又經常用到的物件、工具類物件、頻繁訪問資料庫或檔案的物件(比如資料來源、 session工廠 ~等)
一、餓漢式(靜態常量)
優缺點說明:
1)、 優點:這種寫法比較簡單,就是在類裝載的時候就完成例項化。避免了執行緒同步問題。
2) 、缺點:在類裝載的時候就完成例項化,沒有達到Lazy Loading的效果。 如果從始至終從未使用過這個例項,則 會造成記憶體的浪費
結論:這種單例模式可用,可能造成記憶體浪費
public class Mgr01 {
private static final Mgr01 INSTANCE = new Mgr01();
private Mgr01(){}
public static Mgr01 getInstance(){ return INSTANCE; }
public void m(){ System.out.println("m"); }
public static void main(String[] args) {
Mgr01 mg1 = Mgr01.getInstance();
Mgr01 mg2 = Mgr01.getInstance();
System.out.println(mg1.hashCode()+"\n"+mg2.hashCode());
}
}
二、懶漢式、雙重檢查
1)、 Double-Check概念 是多執行緒開發中常使用到的,如程式碼中所示,我們進行了兩次if (singleton == nul)檢 查,這樣就可以保證執行緒安全了。
2)、這樣,例項化程式碼只用執行一次,後面再次訪問時,判斷if (singleton == nlll),直接return例項化物件,也 避免的反覆進行方法同步.
3)、執行緒安全;延遲載入;效率較高
4)、結論:在實際開發中,推薦使用這種單例設計模式
public class Mgr03 {
public class Mgr03 {
private static Mgr03 INSTANCE;
private Mgr03(){}
public static Mgr03 getInstance(){
if(INSTANCE == null){
synchronized (Mgr03.class) {
if(INSTANCE == null){
INSTANCE = new Mgr03();
return INSTANCE;
}
}
}
return INSTANCE;
}
}
三、靜態內部類
1)、這種方式採用了類裝載的機制來保證初始化例項時只有一個執行緒。
2)、靜態內部類方式在Singleton類被裝載時並不會立即例項化,而是在需要例項化時,呼叫getInstance方 法, 才會裝載SingletonInstance類,從而完成Singleton的例項化。
3)、類的靜態屬性只會在第一次載入類的時候初始化,所以在這裡,JVM幫助我們保證了執行緒的安全性,在類 進行初始化時,別的執行緒是無法進入的。
4)、優點:避免了執行緒不安全,利用靜態內部類特點實現延遲載入,效率高
5) 、結論:推薦使用.
- volatile關鍵字的作用:保證了變數的可見性(visibility)。被volatile關鍵字修飾的變數,如果值發生了變更,其他執行緒立馬可見,避免出現髒讀的現象。如以下程式碼片段,isShutDown被置為true後,doWork方法仍有執行。如用volatile修飾isShutDown變數,可避免此問題。
public class Mgr04 {
private static volatile Mgr04 INSTANCE;
private Mgr04(){}
private static class MgrSon{
private static final Mgr04 INSTANCES = new Mgr04();
}
public static Mgr04 getInstance(){
try{
Thread.sleep(1);
}catch(Exception e){
e.printStackTrace();
}
return MgrSon.INSTANCES;
}
public static void main(String[] args) {
for(int i=0;i<1000;i++){
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(getInstance().hashCode());
}
}).start();
}
}
}
四、列舉
1)、這藉助JDK1.5中新增的列舉來實現單例模式。不僅能避免多執行緒同步問題,而且還能防 止反序列化重新 建立新的物件。
2)、這種方式是Effective Java作者Josh Bloch提倡的方式
3)、結論:推薦使用
//語法上是最好的,但是是一個列舉,java的創始人之一寫的書上的Effective推薦的寫法
public enum Mgr {
INSTANCE;
public static void main(String[] args) {
for(int i=0;i<1000;i++){
//有laman簡寫 效果是一樣的 用於實現只有一個方法的介面 ()->{}
new Thread(new Runnable(){
@Override
public void run() {
System.out.println(INSTANCE.hashCode());
}
}).start();
}
}
}
2. 工廠模式、
1)、工廠 模式的意義
將例項化物件的程式碼提取出來,放到-一個類中統一管理和維護, 達到和主專案的
依賴關係的解耦。從而提高專案的擴充套件和維護性。
2)、三種工廠 模式(簡單工廠模式、工廠方法模式、抽象工廠“模式)
3)、設計 模式的依賴抽象原則
➢ 建立物件例項時,不要直接new類,而是把這個new類的動作放在一一個工廠:的方法中,並返回。有的書上說, 變數不要直接持有具體類的引用。
➢不要讓類繼承具體類,而是繼承抽象類或者是實現interface(介面)
➢不要覆蓋基類中已經實現的方法。
1. 簡單工廠模式
基本介紹:
1)、簡單工廠模式是屬於建立型模式,是工廠模式的一種。簡單工廠模式是由一個工廠物件決定創建出哪一種產 品類的例項。簡單工廠模式是工廠模式家族中最簡單實用的模式
2)、簡單工廠模式:定義了一個建立物件的類,由這個類來封裝例項化物件的行為(程式碼)
3)、在軟體開發中,當我們會用到大量的建立某種、某類或者某批物件時,就會使用到工廠模式
優點是比較好理解,簡單易操作。
缺點是違反了設計模式的ocp原則,即對擴充套件開放,對修改關閉。即當我們給類增加新功能的時候,儘量不修 改程式碼,或者儘可能少修改程式碼.
//披薩類
public abstract class Pizza {
String name;
public void prepare(){ }
public void setName(String name){
this.name=name;
}
public void bake(){
System.out.println(name+"------正在烤");
}
public void cut(){
System.out.println(name+"------正在切!");
}
public void box(){
System.out.println(name+"------打包!");
}
}
public class PepperPizza extends Pizza{
//披薩種類
public void prepare(){
setName("胡椒披薩");
System.out.println("PepperPizza");
}
}
//製作披薩的工廠,只負責製造披薩
public class PizzaFactory {
public Pizza createPizza(String orderType){
Pizza piza = null;
if(orderType.equals("Greek")){
piza = new GreekPizza();
}else if(orderType.equals("Cheese")){
piza = new CheesePizza();
}else if(orderType.equals("Pepper")){
piza = new PepperPizza();
}
return piza;
}
}
//使用工廠製造披薩的類,建立披薩的方式有很多種,這個算是烤的,可能還有冷的、麻辣、香辣、甜...
public class OrdType {
PizzaFactory pizzaFactory;
Pizza pizza;
OrdType(PizzaFactory pizzafact){
this.pizzaFactory = pizzafact;
setPizzaFactory();
}
public void setPizzaFactory(){
String order = "";
do{
order = getType();
pizza = pizzaFactory.createPizza(order);
if(pizza != null){
System.out.println("歡迎製作披薩!");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("製作完成!");
}else{
System.out.println("訂購披薩失敗,沒有您需要的披薩");
break;
}
}while(true);
}
public String getType(){
try{
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch(Exception e){
e.printStackTrace();
}
return "";
}
}
//真正使用的類,可能是控制器,服務層
public class MakePizza {
public static void main(String[] args) {
new OrdType(new PizzaFactory());
System.out.println("Make pizza over!");
}
}
2. 工廠方法模式
工廠方法模式介紹:
1. 工廠方法模式設計方案:將披薩專案的例項化功能抽象成抽象方法,在不同的口味點餐子類中具體實現。
2. 工廠方法模式:定義了一個建立物件的抽象方法,由子類決定要例項化的類。工廠方法模式將物件的例項化推遲到子類。
public abstract class Pizza {
String name;
public abstract void prepare();
public void setName(String name){
this.name=name;
}
public void bake(){
System.out.println(name+"------正在烤");
}
public void cut(){
System.out.println(name+"------正在切!");
}
public void box(){
System.out.println(name+"------打包!");
}
}
public class BJCheesePizza extends Pizza{
public void prepare(){
setName("北京乳酪披薩");
System.out.println(name);
}
}
public class BJGreekPizza extends Pizza{
public void prepare(){
setName("北京希臘披薩");
System.out.println(name);
}
}
//使用工廠製造披薩的類,建立披薩的方式有很多種,這個算是烤的,可能還有冷的、麻辣、香辣、甜...
public abstract class OrdType {
public abstract Pizza setPizzaFactory(String order);
//公共的方法由抽象的構造器寫出來
OrdType(){
Pizza pizza = null;
String order = null;
do{
order = getType();
pizza = setPizzaFactory(order);//具體方法由子類實現
if(pizza == null){
break;
}
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("您的"+pizza.name+"請收好!");
}while(true);
}
public String getType() {
try{
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch(Exception e){
e.printStackTrace();
}
return "";
}
}
public class BJOrdType extends OrdType{
@Override
public Pizza setPizzaFactory(String order) {
Pizza pizza = null;
if(order.equals("Cheese")){
pizza = new BJCheesePizza();
}else if(order.equals("Greek")){
pizza = new BJGreekPizza();
}else{
System.out.println("沒有你要的種類");
}
return pizza;
}
}
//製作披薩的抽象工廠類,
public abstract class PizzaFactory {
public abstract Pizza createPizza(String orderType);
}
public class BJPizzaFactory extends PizzaFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("Cheese")){
pizza = new BJCheesePizza();
}else{
System.out.println("您要的披薩不存在!");
}
return pizza;
}
}
public class TestBjPizza {
public static void main(String[] args) {
new BJOrdType();
System.out.println("結束!");
}
}
3. 抽象工廠模式
1)、抽象工廠模式:定義了一個interface用於建立相關或有依賴關係的物件簇,而無需指明具體的類
2)、抽象工廠模式可以將簡單工廠模式和工廠方法模式進行整合。
3)、從設計層面看,抽象工廠模式就是對簡單工廠模式的改進(或者稱為進- -步 的抽象)。
4)、將工廠抽象成兩層,AbsFactory(抽象工廠) 和具體實現的工廠子類。程式設計師可以根據建立物件型別使用對 應的工廠子類。這樣將單個的簡單工廠類變成了工廠簇,更利於程式碼的維護和擴充套件。
5)、類圖
public abstract class Pizza {
String name;
public abstract void prepare();
public void setName(String name){
this.name=name;
}
public void bake(){
System.out.println(name+"------正在烤");
}
public void cut(){
System.out.println(name+"------正在切!");
}
public void box(){
System.out.println(name+"------打包!");
}
}
public class BJCheesePizza extends Pizza{
public void prepare(){
setName("北京乳酪披薩");
System.out.println(name);
}
}
public class BJGreekPizza extends Pizza{
public void prepare(){
setName("北京希臘披薩");
System.out.println(name);
}
}
//介面
public interface IPizzaFactory {
Pizza createPizza(String orderType);
}
public class BJFactory implements IPizzaFactory{
@Override
public Pizza createPizza(String orderType) {
Pizza pizza = null;
if(orderType.equals("Cheese")){
pizza = new BJCheesePizza();
}else if(orderType.equals("Greek")){
pizza = new BJGreekPizza();
}else{
System.out.println("您要的披薩不存在!");
}
return pizza;
}
}
public class BJOrdType {//北京工廠子類,倫敦的同理
IPizzaFactory iPizzaFactory;
OrdType(IPizzaFactory Factory){
setiPizzaFactory(Factory);
}
//只要是成員變數都是要有set方法的,不然就沒有意義
private void setiPizzaFactory(IPizzaFactory factory) {
this.iPizzaFactory = factory;
Pizza pizza= null;
String order = null;
do{
order = getType();//這裡的工廠可能是北京的,也有可能是倫敦的
pizza = iPizzaFactory.createPizza(order);
if(pizza == null){
break;
}
System.out.println("使用抽象工廠模式");
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
System.out.println("您的"+pizza.name+"請收好!");
}while(true);
}
public String getType() {
try{
BufferedReader strin = new BufferedReader(new InputStreamReader(System.in));
System.out.println("input pizza type:");
String str = strin.readLine();
return str;
}catch(Exception e){
e.printStackTrace();
}
return "";
}
}
public class TestFactory {
//測試類
public static void main(String[] args) {
new OrdType(new BJFactory());
System.out.println("結束~!");
}
}
執行結果
input pizza type:
Greek
使用抽象工廠模式
北京希臘披薩
北京希臘披薩------正在烤
北京希臘披薩------正在切!
北京希臘披薩------打包!
您的北京希臘披薩請收好!
input pizza type:
asdf
您要的披薩不存在!
結束~!
4. 原型模式、
1)、原型模式(Prototype模式)是指: 用原型例項指定建立物件的種類,並且通過拷貝這些原型,建立新的物件
2)、原型模式是一種建立型設計模式,允許-一個物件再建立另外-一個可定製的物件無需知道如何建立的細節
3)、工作原理是:通過將-一個原型物件傳給那個要發動建立的物件,這個要發動建立的物件通過請求原型物件 拷貝它們自己來實施建立,即物件.clone()
4)、形象的理解:孫大聖拔出猴毛,變出其它孫大聖
原型模式實現類的克隆,如果說後面類多了一個屬性,克隆的屬性依然會有。
① 淺拷貝關於物件是引用的。
- 對於資料型別是基本資料型別的成員變數,淺拷貝會直接進行值傳遞,也就是將
該屬性值複製- - 份給新的物件。 - 對於資料型別是引用資料型別的成員變數,比如說成員變數是某個陣列、某個類
的物件等,那麼淺拷貝會進行引用傳遞,也就是隻是將該成員變數的引用值(內
存地址)複製一.份給新的物件。因為實際上兩個物件的該成員變數都指向同-一個
例項。在這種情況下,在一個物件中修改該成員變數會影響到另一個物件的該成
員變數值 - 淺拷貝是使用預設的clone()方法來實現
sheep = (Sheep) super.clone();
// 需要實現Cloneable介面
public class Sheep implements Cloneable{
private int age;
private String name;
private String color;
public Sheep friend;
@Override //從寫Object的clone類
protected Object clone(){ //如果屬性只有基本資料型別,那麼預設超類方法即可
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();
}catch(Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
}
public static void main(String[] args) {
Sheep sheep = new Sheep("tom",1,"white");
sheep.friend = new Sheep("jack",2,"black");
Sheep sheep1 = (Sheep)sheep.clone();
Sheep sheep2 = (Sheep)sheep.clone();
Sheep sheep3 = (Sheep)sheep.clone();
System.out.println(sheep+"------ sheep.friend="+sheep.friend.hashCode());
System.out.println(sheep1+"------ sheep.friend="+sheep1.friend.hashCode());
System.out.println(sheep2+"------ sheep.friend="+sheep2.friend.hashCode());
System.out.println(sheep3+"------ sheep.friend="+sheep3.friend.hashCode());
}
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
Sheep [age=1, name=tom, color=white]------ sheep.friend=5433634
② 深拷貝基本介紹
- 複製對 象的所有基本資料型別的成員變數值
- 為 所有引用資料型別的成員變數申請儲存空間,並複製每個引用資料型別成員變
量所引用的物件,直到該物件可達的所有物件。也就是說,物件進行深拷貝要對
整個物件進行拷貝 - 深拷貝實現方式1: 重寫clone方法來實現深拷貝,
- 深拷貝實現方式2: 通過物件序列化實現深拷貝
方式一:
public class Sheep implements Cloneable,Serializable{
private int age;
private String name;
private String color;
public Sheep1 friend;
@Override
protected Object clone(){
Sheep sheep = null;
try{
sheep = (Sheep)super.clone();
sheep.friend = (Sheep1)friend.clone();
}catch(Exception e){
System.out.println(e.getMessage());
}
return sheep;
}
//friend 引用指向重寫父類clone方法
public class Sheep1 implements Serializable,Cloneable{
String sheep1 = "sheep1";
@Override
public String toString() {
return "Sheep1 [sheep1=" + sheep1 + "]";
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
方式二、
- 使用的是流的方式,進行序列化和反序列化,轉成位元組,在轉成物件,即時位元組是一樣的,但是轉出來的物件hashCode()也會不同,
public class Sheep1 implements Serializable{
String sheep1 = "sheep1";
@Override
public String toString() {
return "Sheep1 [sheep1=" + sheep1 + "]";
}
}
//只要實現Serializable介面就行了。
public class Sheep implements Serializable{
private int age;
private String name;
private String color;
public Sheep1 friend;
//方式二、運用流存到記憶體的序列化原理,進行讀寫操作來克隆
Sheep copy(){
ObjectInputStream ois = null;
ByteArrayInputStream bais = null;
ObjectOutputStream oos = null;
ByteArrayOutputStream baos = null;
try{//可以這樣理解,物件轉換到位元組叫序列化,寫出去叫out
baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
//write is a Object,this is 當前 Object
oos.writeObject(this);
//相當於轉成了位元組,然後把位元組轉成Object回來,叫反序列化,
//baos.toByteArray()意思是拷貝一份剛剛序列化的
bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
//讀的是Object method name is readObject()
Sheep sheep = (Sheep)ois.readObject();
return sheep;
}catch(Exception e){
System.out.println(e.getMessage());
}
try{//記得要關閉流
ois.close();
bais.close();
oos.close();
baos.close();
}catch(Exception e){
e.printStackTrace();
}
return null;
}
}
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=21598637
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=26887603
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=10069385
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=3615232
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=14519747
Sheep [age=1,name=tom,color=white,friend=Sheep1 [sheep1=sheep1]]sheep.friend=17033014
原型模式的優缺點:
1)、建立新的物件比較複雜時,可以利用原型模式簡化物件的建立過程,同時也能夠提高效率
2)、 不用重新初始化物件,而是動態地獲得物件執行時的狀態
3) 、如果原始物件發生變化(增加或者減少屬性),其它克隆物件的也會發生相應的編號,無需修改程式碼
4) 、在實現深克隆的時候可能需要比較複雜的程式碼
5) 、缺點:需要為每- -個類配備-一個克隆方法,這對全新的類來說不是很難,但對已有的類進行改造時,需要 修改其原始碼,違背了ocp原則,這點請同學們注意.
5. 建造者模式、
- 建造者模式的注意事項和細節
1)、客戶端(使用程式)不必知道產品內部組成的細節,將產品本身與產品的建立過程解耦,使得相同的建立過程 可以建立不同的產品物件
2)、每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,使用者 使用不同的具體建造者即可得到不同的產品物件
3)、可以更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不同的方法中,使得建立過程更加清晰,也更方便使用程式來控制建立過程
4)、增加新的具體建造者無須修改原有類庫的程式碼,指揮者類針對抽象建造者類程式設計,系統擴充套件方便,符合“開閉原則”
5)、建造者模式所建立的產品一般具有較多的共同點,其組成部分相似,如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用範圍受到—定的限制。
6)、如果產品的內部變化複雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大,因此在這種情況下,要考慮是否選擇建造者模式.
7)、抽象工廠模式VS建造者模式抽象工廠模式實現對產品家族的建立,一個產品家族是這樣的一系列產品:具有不同分類維度的產品組合,採用抽象工廠模式不需要關心構建過程,只關心什麼產 品由什麼工廠生產即可。而建造者模式則是要求按照指定的藍圖建造產品,它的主要目的是通過組裝零配件而產生一個新產品
建造者模式的四個角色:
1)、 Product (產品角色) : - 一個 具體的產品物件。
2) 、Builder (抽象建造者) :建立一個Product物件的各個部件指定的介面/抽象類。
3)、 ConcreteBuilder (具體建造者) :實現介面,構建和裝配各個部件。
4) 、Director (指揮者) :構建- -個使用Builder介面的物件。它主要是用於建立- -個複雜的物件。它主要 有 兩個作用,一是:隔離了客戶與物件的生產過程,二是:負責控制產品物件的生產過程。
public class House {
private String baise;
private String wall;
private String roofed;
//補上set、get、toString方法
}
public abstract class HouseBuilder {
//建造房子的抽象類
protected House house = new House();
public House getHouse() {
return house;
}
public abstract void builderBaise();
public abstract void builderwall();
public abstract void roofed();
}
public class CommonHouse extends HouseBuilder{
//普通房子
@Override
public void builderBaise() {
house.setBaise("地基5m!");
System.out.println("打普通地基5m深!");
}
@Override
public void builderwall() {
house.setWall("砌牆10cm!");
System.out.println("砌普通牆10cm厚!");
}
@Override
public void roofed() {
house.setRoofed("屋頂3m!");
System.out.println("蓋普通屋頂3m高!");
}
}
public class HigthBuilder extends HouseBuilder {
//高樓
@Override
public void builderBaise() {
house.setBaise("地基25m!");
System.out.println("打高樓地基25m深!");
}
@Override
public void builderwall() {
house.setWall("砌牆20m!");
System.out.println("砌高樓牆20cm厚!");
}
@Override
public void roofed() {
house.setRoofed("屋頂9m!");
System.out.println("蓋高樓屋頂9m高!");
}
}
public class DirectorHouse {
//指揮者
HouseBuilder houseBuilder = null;
DirectorHouse(HouseBuilder houseBuild){
this.houseBuilder = houseBuild;
}
public void setHouseBuilder(HouseBuilder houseBuild) {
this.houseBuilder = houseBuild;
}
//建造順序由指揮者來決定
House BuilderHouse(){
houseBuilder.builderBaise();
houseBuilder.builderwall();
houseBuilder.roofed();
return houseBuilder.getHouse();
}
}
public class Client {
//蓋房子的客戶 需求方
public static void main(String[] args) {
//蓋普通房子
DirectorHouse dh = new DirectorHouse(new CommonHouse());
dh.setHouseBuilder(new CommonHouse());
House house = dh.BuilderHouse();
System.out.println(house+"\n-----------------------------");
//蓋高樓
dh.setHouseBuilder(new HigthBuilder());
System.out.println(dh.BuilderHouse());
}
}
2)、結構型模式
2 結構型模式: 介面卡模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
1. 介面卡模式
-
基本介紹
1)、介面卡模式(Adapter Pattern)將某個類的介面轉換成客戶端期望的另一個介面表示,主的目的是相容 性,讓原本因介面不匹配不能一起工作的兩個類可以協同工作。其別名為包裝器(Wrapper)
2)、介面卡模式屬於結構型模式
3)、主要分為三類: 類介面卡模式、物件介面卡模式、介面介面卡模式 -
工作原理
1)、 介面卡模式:將一個類的介面轉換成另一種介面.讓原本介面不相容的類可以相容
2)、從使用者的角度看不到被適配者,是解耦的
3)、使用者呼叫介面卡轉化出來的目標介面方法,介面卡再呼叫被適配者的相關介面方法 -
介面卡模式的注意事項和細節
1)、三種命名方式,是根據src是以怎樣的形式給到Adapter (在Adapter裡的形式)來命名的。
2)、類介面卡:以類給到,在Adapter裡, 就是將src當做類,繼承物件介面卡:以物件給到,在Adapter 裡,將src作為-一個物件,持有介面介面卡:以介面給到,在Adapter裡,將srC作為一個介面,實現
3)、 Adapter模式最大的作用還是將原本不相容的介面融合在- - 起工作。
4)、實際開發中,實現起來不拘泥於我們講解的三種經典形式
① 類介面卡模式、
- 類介面卡模式注意事項和細節
1)、 Java是 單繼承機制,所以類介面卡需要繼承src類這一點算是- 一個缺點,因為這要求dst必須是介面, 有一-定侷限性;
2) 、src類的方法在Adapter中都會暴露出來,也增加了使用的成本。
3)、由於其繼承了src類,所以它可以根據需求重寫src類的方法,使得Adapter的靈活性增強了。
public class Voltage220v {
//電源電壓
public int Voltage220(){
int src = 220;
System.out.println("獲得電壓"+src+"伏");
return src;
}
}
public interface Voltage {
//電源轉換介面
int charging5v();
int charging20v();
}
public class VoltageAdatper extends Voltage220v implements Voltage{
//電源轉介面
public int charging5v() {
int src = Voltage220();
return src/44;
}
public int charging20v() {
return 0;
}
}
public class Phone {
//手機
void charging(Voltage voltage5v){
int src = voltage5v.charging5v();
if(src == 5){
System.out.println("轉成5 伏,充電成功!!");
}else{
System.out.println("轉化失敗!");
}
}
}
public class Client {
//客戶
public static void main(String[] args) {
Phone ph = new Phone();
ph.charging(new VoltageAdatper());
System.out.println("Charging over!");
}
}
② 物件介面卡模式、
- 物件介面卡模式注意事項和細節
1)、物件介面卡和類介面卡其實算是同- -種思想,只不過實現方式不同。根據合成複用原則,使用組合替 代繼承,所以它解決 了類介面卡必須繼承src的侷限性問題,也不再要求dst必須是介面。
2) 、使用成本更低,更靈活。
public class Voltage220v {
//電源電壓
public int Voltage220(){
int src = 220;
System.out.println("獲得電壓"+src+"伏");
return src;
}
}
public interface Voltage {
//電源轉換介面
int charging5v();
int charging20v();
}
public class VoltageAdatper implements Voltage{
//電源轉介面 使用的是聚合關係
protected Voltage220v voltage220v;
VoltageAdatper(Voltage220v voltage220){
this.voltage220v = voltage220;
}
public int charging5v() {
if(voltage220v == null){
System.out.println("exit!");
return;
}
int src = voltage220v.Voltage220();
return src/44;
}
public int charging20v() {
return 0;
}
}
public class Phone {
//手機
void charging(Voltage voltage5v){
int src = voltage5v.charging5v();
if(src == 5){
System.out.println("轉成5 伏,充電成功!!");
}else{
System.out.println("轉化失敗!");
}
}
}
public class Client {
//客戶
public static void main(String[] args){
System.out.println("物件介面卡");
Phone ph = new Phone();
VoltageAdatper v = new VoltageAdatper(new Voltage220v());
ph.charging(v);
System.out.println("Charging over!");
}
}
③ 介面介面卡模式、
- 介面介面卡模式介紹
1)、一些 書籍稱為:介面卡模式(Default Adapter Pattern)或預設介面卡模式。
2)、當不需要全部實現介面提供的方法時,可先設計一個抽象類實現介面,併為該介面中每個方法提供一 個預設實現(空方法),那麼該抽象類的子類可有選擇地覆蓋父類的某些方法來實現需求
3)、適用於一個介面不想使用其所有的方法的情況。
public interface Interface {
void m1();
void m2();
void m3();
void m4();
}
public abstract class AbstartAdatper implements Interface {
@Override
public void m1(){ }
@Override
public void m2(){ }
@Override
public void m3(){ }
@Override
public void m4(){ }
}
public class Client {
public static void main(String[] args) {
AbstartAdatper aba = new AbstartAdatper(){
@Override
public void m1(){
System.out.println("介面介面卡");
}
};
aba.m1();
}
}
2. 橋接模式
3. 裝飾模式
4. 組合模式
5. 外觀模式
6. 享元模式
7. 代理模式
- 代理模式的基本介紹
1)、代理模式:為一個物件提供-一個替身,以控制對這個物件的訪問。即通過代理物件訪問目標物件.這樣 做的好處是:可以在目標物件實現的基礎上,增強額外的功能操作,即擴充套件目標物件的功能。
2)、被代理的物件可以是遠端物件、建立開銷大的物件或需要安全控制的物件
3)、代理模式有不同的形式,主要有三種靜態代理、動態代理(JDK代理、介面代理)和Cglib代理(可以在內 存動態的建立物件,而不需要實現介面,他是屬於動態代理的範疇)。
①、靜態代理
-
靜態程式碼模式的基本介紹
靜態代理在使用時,需要定義介面或者父類,被代理物件(即目標物件)與代理物件一起實現相同的介面或者是繼承相同父類
應用例項:
➢具體要求
1)、定義一個介面:ITeacherDao
2)、目標物件TeacherDAO實現介面ITeacherDAO
3) 、使用靜態代理方式,就需要在代理物件TeacherDAOProxy中也實現ITeacherDAO
4) 、呼叫的時候通過呼叫代理物件的方法來呼叫目標物件.
5) 、特別提醒:代理物件與目標物件要實現相同的介面,然後通過呼叫相同的方法來呼叫目標物件的方法。 -
靜態代理優缺點
1)、優點:在不修改目標物件的功能前提下,能通過代理物件對目標功能擴充套件.
2)、缺點:因為代理物件需要與目標物件實現一樣的介面,所以會有很多代理類
3)、一旦介面增加方法,目標物件與代理物件都要維護
public interface ITeacher {
void teacher();
}
public class Teacher implements ITeacher{
//老師類
public void teacher() {
System.out.println("語文老師原教課內容!");
}
}
public class TeacherProxy implements ITeacher{
private ITeacher iTeacher;
TeacherProxy(ITeacher iTeacher){
this.iTeacher = iTeacher;
}
public void teacher(){
System.out.println("新老師擦黑板!");
iTeacher.teacher();
System.out.println("下課了,關上門走了!");
}
}
public class Client {
public static void main(String[] args) {
new TeacherProxy(new Teacher()).teacher();
}
}
②、動態代理
- 動態代理模式的基本介紹
1)、代理物件,不需要實現介面,但是目標物件要實現介面,否則不能用動態代理
2)、代理物件的生成,是利用JDK的API,動態的在記憶體中構建代理物件
3)、動態代理也叫做: JDK代理、 介面代理 - JDK中生成代理物件的API
1)、代理類所在包:java.lang.reflect.Proxy
2) 、JDK實現代理 只需要使用newProxyInstance方法,但是該方法需要接收三個引數,完
整的寫法是: .
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )- 引數1: loader , 類載入器,動態代理類執行時建立,任何類都需要類載入器將其載入到記憶體。
一般情況: 當前類.class . getClassLoader();
目標類例項 .getClass().getInterface(); - 引數2: Class[] interfaces 代理類需要實現的所有介面
方式1:**目標類例項. getClass(). getInterfaces() ;**注意: 只能獲得自己介面,不能獲得父元素介面
方式2: new Class []{UserService.class}
例如: jdbc驅動–> DriverManager獲得介面 Connection - 引數3: InvocationHandler處理類, 介面,必須進行實現類,-般採用匿名內部
提供invoke方法,代理類的每一個方法執行時,都將呼叫一次invoke- 引數3.1: Object proxy :代理物件
- 引數3.2: Method method :代理 物件當前執行的方法的描述物件(反射)
執行方法名: method. getName( )
執行方法: method. invoke(物件,實際引數) - 引數3.3: 0bject[] args :方法實際引數
- 引數1: loader , 類載入器,動態代理類執行時建立,任何類都需要類載入器將其載入到記憶體。
public interface ITeacher {
void teacher();
void mathTea();
}
public class Teacher implements ITeacher{
//老師類
public void teacher() {
System.out.println("語文老師原教課內容!");
}
public void mathTea(){
System.out.println("數學老師原教課內容!");
}
}
public class ProxyFactory {
private ITeacher iTeacher;
ProxyFactory(ITeacher iTeacher){
this.iTeacher = iTeacher;
}
public Object getInstance(){
return Proxy.newProxyInstance(
ITeacher.class.getClassLoader(),
iTeacher.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("執行老師方法前!");
Object obj = method.invoke(iTeacher, args);
System.out.println("當然這句話可以執行其他方法!");
return obj;
}
});
}
}
public class Client {
public static void main(String[] args) {
ITeacher iTeacher = (ITeacher)new ProxyFactory(new Teacher()).getInstance();
iTeacher.mathTea();
iTeacher.teacher();
}
}
③、Cglib代理
-
Cglib代理模式的基本介紹
1)、靜態代理和JDK代理模式都要求目標物件是實現一個介面,但是有時候目標物件只是一個單獨的物件, 並沒有實現任何的介面,這個時候可使用目標物件子類來實現代理這就是Cglib代理
2) 、Cglib代理 也叫作子類代理,它是在記憶體中構建一個子類 物件從而實現對目標物件功能擴充套件,有些書也 將Cglib代理歸屬到動態代理。
3)、 Cglib是一 一個強大的高效能的程式碼生成包,它可以在執行期擴充套件java類與實現java介面.它廣泛的被許 多AOP的框架使用,例如Spring AOP,實現方法攔截
4)、在AOP程式設計中如何選擇代理模式:1. **目標物件需要實現介面,用JDK代理** 2. **目標物件不需要實現介面,用Cglib代理**
5)、Cglib包的底層是通過使用位元組碼處理框架ASM來轉換位元組碼並生成新的類
2020-4-6日在Mybatis 中找的jar包也可以執行
asm-3.3.1.jar
cglib-2.2.2.jar
commons-logging-1.1.1.jar
public class TeacherDao {
public void teacher(){
System.out.println("教書,cglibProxy not need Interface!");
}
public void mathTeacher(){
System.out.println("Math class.cglibProxy not need Interface!");
}
}
public class ProxyFactory implements MethodInterceptor{
private Object object;
ProxyFactory(Object Obj){
this.object = Obj;
}
public void setTeacherDao(Object Obj) {
this.object = Obj;
}
public Object getInstance(){
//1. 建立一個工具類
Enhancer enhancer = new Enhancer();
//2. 拿到他的class,設定它的父類。
enhancer.setSuperclass(object.getClass());
//3. 設定回撥函式。
enhancer.setCallback(this);
//4. 建立子類物件,及代理物件。
return enhancer.create();
}
@Override
public Object intercept(Object arg0, Method method, Object[] arg2,MethodProxy arg3) throws Throwable {
// 未知 方法 形參
//抽象類底層封裝的自動呼叫 intercept,預設對沒個方法都進行了封裝
System.out.println("開始呼叫Cglib代理物件!");
Object obj = method.invoke(object, arg2);
System.out.println("呼叫代理物件結束。");
return null;
}
}
public class Client {
public static void main(String[] args) {
ProxyFactory proxy = new ProxyFactory(new TeacherDao());
TeacherDao tea = (TeacherDao)proxy.getInstance();
tea.mathTeacher();
tea.teacher();
}
}
④、常見代理模式介紹
1)、防火牆代理 .
內網通過代理穿透防火牆,實現對公網的訪問。
2)、快取代理
比如:當請求圖片檔案等資源時,先到快取代理取,如果取到資源則ok,如果取不到資源,再到公網或者數 據庫取,然後快取。
3)、遠端代理
遠端物件的本地代表,通過它可以把遠端物件當本地物件來呼叫。遠端代理通過網路和真正的遠端物件 溝通訊息。
4)、同步代理:主要使用在多執行緒程式設計中,完成多執行緒間同步工作
3)、行為型模式
3)行為型模式: 模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、直譯器模式(Interpreter模式) 、狀態模式、策略模式、職責鏈模式(責任鏈模式)。
1. 模版方法模式
-
基本介紹.
1)、模板方法模式(Template Method Pattern),又叫模板模式(Template Pattern), Z在一個抽象類公開 定義了執行它的方法的模板。它的子類可以按需要重寫方法實現,但呼叫將以抽象類中定義的方式進 行。
2)、簡單說,模板方法模式定義一個操作中的演算法的骨架,而將- - 些步驟延遲到子類中,使得子類可以不 改變-一個演算法的結構,就可以重定義該演算法的某些特定步驟
3)、這種型別的設計模式屬於行為型模式。 -
模板方法模式的注意事項和細節
1)、基本思想是:演算法只存在於一個地方,也就是在父類中,容易修改。需要修改演算法時,只要修改父類 的模板方法或者已經實現的某些步驟,子類就會繼承這些修改
2)、實現了最大化程式碼複用。父類的模板方法和已實現的某些步驟會被子類繼承而直接使用。
3)、 既統- -了 演算法,也提供了很大的靈活性。父類的模板方法確保 了演算法的結構保持不變,同時由子類 提供部分步驟的實現。
4)、該模式的不足之處:每一個不同的實現都需要-一個子類實現,導致類的個數增加,使得系統更加龐大
5)、一般模板方法都加上final關鍵字,防止子類 重寫模板方法.
6)、模板方法模式使用場景:當要完成在某個過程,該過程要執行一系列步驟,這一系列的步驟基本相 同,但其個別步驟在實現時可能不同,通常考慮用模板方法模式來處理模板方法模式的注意事項和細節
① 普通模板方法模式
public abstract class SoyaMilk {
//final can't writer son class
final void make(){
System.out.println("Making Soya Milk start!");
select();
add();
soak();
beat();
}
abstract void select();
final void add(){
System.out.println("add Make Milk material!");
}
final void soak(){
System.out.println("soak on the water!");
}
final void beat(){
System.out.println("beat over finish!");
}
}
public class Pennut extends SoyaMilk{
@Override
void select() {
System.out.println("select big Soya!");
}
}
public class Client {
public static void main(String[] args) {
SoyaMilk soyaMilk = new Pennut();
soyaMilk.make();
}
}
② 鉤子方法
- 模板方法模式的鉤子方法.
1)、在模板方法模式的父類中,我們可以定義一個方法,它預設不做任何事,子類可以視情況要不要覆蓋 它,該方法稱為“鉤子”。
2)、還是用上面做豆漿的例子來講解,比如,我們還希望製作純豆漿,不新增任何的配料,請使用鉤子方 法對前面的模板方法進行改造
public abstract class SoyaMilk {
//final can't writer son class
final void make(){
select();
if(isPure()){
add();
}
soak();
beat();
}
abstract void select();
final void add(){
System.out.println("add Make Milk material!");
}
final void soak(){
System.out.println("soak on the water!");
}
final void beat(){
System.out.println("beat over finish!");
}
//可以設定需要還是不需要呼叫的方法
boolean isPure(){
return true;
}
}
public class PureMilk extends SoyaMilk{
@Override
void select() {
System.out.println("select big Soya!");
}
@Override
boolean isPure(){
return false;
}
}
public class Client {
public static void main(String[] args) {
SoyaMilk soyaMilk = new Pennut();
soyaMilk.make();
System.out.println("--------------------------");
SoyaMilk pureMilk = new PureMilk();
pureMilk.make();
}
}
2. 命令模式
3. 訪問者模式
4. 迭代器模式
5. 觀察者模式
6. 中介者模式
7. 備忘錄模式
8. 直譯器模式(Interpreter模式)
9. 狀態模式
10. 策略模式
- 基本介紹
1)、策略模式(Strategy Pattern)中,定義演算法族,分別封裝起來,讓他們之間可以互相替換,此模式讓算 法的變化獨立於使用演算法的客戶
2)、這演算法體現了幾個設計原則, - 第一、把變化的程式碼從不變的程式碼中分離出來;
- 第二、針對介面程式設計而不具體類(定義了策略介面) ;
- 第三、多用組合/聚合少用繼承(客戶通過組合方式使用策略)。
策略模式:分別封裝行為介面,實現演算法族,超類裡放行為介面物件,在子類裡具體
設定行為物件。原則就是: 分離變化部分,封裝介面,基於介面程式設計各種功能。此模
式讓行為的變化獨立於演算法的使用者
public abstract class Duck {
Flybeha flybeha;
Duck(){
disPlay();
quack();
swim();
}
abstract void disPlay();
void quack(){
System.out.println("鴨子會叫~~~");
}
void swim(){
System.out.println("鴨子會游泳~~~");
}
void fly(){
if(flybeha != null){
flybeha.Fly();
}else{
System.out.println("fly failed");
}
}
public void setFlybeha(Flybeha flybeha) {
this.flybeha = flybeha;
}
}
public interface Flybeha {
//接口裡面寫上所有鴨子的飛翔能力描述,讓子類去實現
//將來要新增什麼鴨子,直接在寫個類實現介面就行了
void Fly();
}
//各種飛翔能力實現類
public class GoodFly implements Flybeha{
@Override
public void Fly() {
System.out.println("this a Duck fly very high!");
}
}
public class NoFly implements Flybeha{
@Override
public void Fly() {
System.out.println("該物種不能飛!");
}
}
public class BadFly implements Flybeha {
@Override
public void Fly() {
System.out.println("this Duck fly is bad~");
}
}
//野鴨
public class WildDuck extends Duck{
WildDuck(){
flybeha = new GoodFly();
}
@Override
void disPlay() {
System.out.println("this is a wildDuck");
}
}
public class ToyDuck extends Duck{
//用到的話,改一下實現類就行了,對於擴充套件比較方便
ToyDuck(){
flybeha = new NoFly();
}
@Override
void disPlay() {
System.out.println("this is a ToyDuck");
}
}
● 策略模式的注意事項和細節
1)、策略模式的關鍵是:分析專案中變化部分與不變部分
2)、策略模式的核心思想是:多用組合/聚合少用繼承;用行為類組合,而不是行為的繼承。更有彈性
3)、體現了“對修改關閉,對擴充套件開放”原則,客戶端增加行為不用修改原有程式碼,只要新增一種策略(或者行 為)即可,避免了使用多重轉移語句(if…else if.else)
4)、提供了可以替換繼承關係的辦法:策略模式將 演算法封裝在獨立的Strategy類中使得你可以獨立於其Context 改變它,使它易於切換、易於理解、易於擴充套件
5)、需要注意的是:每新增一個策略就要增加一個類,當策略過多是會導致類數目龐大