文章目錄
GOF(Gang Of Four)—23個設計模式
- 設計模式的分類:
- 創建型:把創建對象的權利交出去,解決的是對象的創建問題。
- 特點:不讓用戶代碼依賴於對象的創建或排列方式,避免用戶直接使用new關鍵創建對象。
- 結構型:描述的是如何將類和對象結合在一起,構成更大的結構。
- 行爲型:描述的是算法和對象間職責的分配,不僅描述對象或類的模式,還描述它們之間的通信方式。
- 創建型:把創建對象的權利交出去,解決的是對象的創建問題。
- 注意:每種模式都用於解決特定的問題域
1 創建型模式
1.1 工廠模式
-
功能:工廠模式主要是爲創建對象提供過渡接口,以便將創建對象的具體過程屏蔽隔離起來,達到提高靈活性的目的。
-
分類
-
工廠模式在《Java與模式》中分爲三類:
- 簡單工廠模式(Simple Factory):不利於產生系列產品;
- 工廠方法模式(Factory Method):又稱爲多形性工廠;
- 抽象工廠模式(Abstract Factory):又稱爲工具箱,產生產品族,但不利於產生新的產品;
這三種模式從上到下逐步抽象,並且更具一般性
-
GOF在《設計模式》一書中將工廠模式分爲兩類:
- 工廠方法模式(Factory Method)
- 抽象工廠模式(Abstract Factory)
- 將簡單工廠模式(Simple Factory)看爲工廠方法模式的一種特例,兩者歸爲一類。
-
工廠模式包括的角色
- 抽象產品類:所創建產品的父類,給出一個抽象接口或抽象類,以及一般由具體產品類具體實現。
- 具體產品類:抽象產品類的實現類,爲實現某個具體產品的對象。
- 抽象工廠類:工廠模式的核心(簡單工廠模式無此抽象類),是具體工廠必須實現的接口或者必須繼承的父類。
- 具體工廠類:繼承抽象工廠類,實現具體業務邏輯。
-
示例(後面程序用到的公共部分):果園員工種植水果(蘋果,香蕉,葡萄)
abstract class Fruit{
private String name;
public Fruit() {
}
public Fruit(String name){
this.name = name;
}
public String toString(){
return "我是"+name;
}
}
class Apple extends Fruit{
public Apple(String name){
super(name);
}
public Apple() {
super("蘋果");
}
}
class Banana extends Fruit{
public Banana(String name) {
super(name);
}
public Banana(){
super("香蕉");
}
}
1.1.1 簡單工廠模式
- 概述
- 又稱爲靜態工廠方法模式。
- 核心是一個具體的類
- 示例代碼
//工廠類(核心)
public class FruitFactory {
public static final String APPLE ="apple";
public static final String BANANA ="banana";
public static Fruit getProduct(String type){
if(APPLE.equals(type)){
return new Apple();
}else if(BANANA.equals(type)){
return new Banana();
}
return null;
}
}
//客戶端
public class client {
public static void main(String[] args) {
Fruit f = FruitFactory.getProduct(FruitFactory.APPLE);
System.out.println(f);
f = FruitFactory.getProduct(FruitFactory.BANANA);
System.out.println(f);
}
}
- 優點 & 缺點
- 優點:如果客戶端需要某種產品,只需向工廠類發送請求,由該類負責所有產品的生產。
- 缺點:如果新增具體產品的時候,需要動到工廠類的代碼,邏輯判斷需要做改動。
1.1.2 工廠方法模式
- 概述
- 它是簡單工廠模式的進一步抽象化和推廣
- 工廠方法模式裏不再只由一個工廠類決定那一個產品類應當被實例化,這個決定被交給抽象工廠的子類去做。
- 核心是一個抽象工廠類
- 示例代碼
//抽象工廠類
public abstract class FruitFactory {
public abstract Fruit getProduct();
}
//具體子類:蘋果工廠
class AppleFactory extends FruitFactory{
@Override
public Fruit getProduct() {
return new Apple();
}
}
//具體子類:香蕉工廠
class BananaFactory extends FruitFactory{
@Override
public Fruit getProduct() {
return new Banana();
}
}
//客戶端
public class client {
public static void main(String[] args) {
FruitFactory fc = new AppleFactory();
Fruit f = fc.getProduct();
System.out.println(f);
fc = new BananaFactory();
System.out.println(fc.getProduct());
}
}
- 優點 & 缺點
- 優點:
- 解決簡單工廠模式有新產品加入時,需要更改原有工廠類的問題.本模式中,一旦有新產品加入,只需要創建與之對應的具體工廠類。
- 符合設計原則,降低了對象之間的耦合度。
- 缺點:
- 系統中存在大量的具體工廠類,且每個工廠類只負責一個子產品的生產,不符合現實世界問題域。
- 不能解決多維產品問題(比如不只生產水果,還生產蔬菜等。。。)
- 優點:
1.1.3 抽象工廠模式
- 概述
- 定義:爲創建一組相關或者是相互依賴的對象提供的一個接口
- 產品族:不同產品類中功能相關聯的產品組成的家族。(崑山基地:生產蘋果和西紅柿;上海基地:生產香蕉和土豆)。崑山基地和上海基地都是一個產品族。
- **工廠方法模式 & 抽象工廠模式 **
-
工廠方法模式
- 一個抽象產品類(Friut),可以派生出多個具體產品類(Apple,Banana等)
- 一個抽象工廠類(FruitFactory),可以派生出多個具體工廠類(AppleFactory,BananaFactory)。
- 每個具體工廠類只能創建一個具體產品類的實例(如AppleFactory只能創建Apple的實例)。
-
抽象工廠模式
- 多個抽象產品類(Friut,Vegetable),每個抽象產品類可以派生出多個具體產品類(如Fruit可以創建出Apple,Banana等)
- 一個抽象工廠類(AbstractFactory),可以派生出多個具體工廠類(KunShanFactory,ShaiHaiFactory)。
- 每個具體工廠類可以創建多個具體產品類的實例(如KunShanFactory可以創建Apple,Tomato的實例)。
- 示例代碼:要生產的產品族(如水果,蔬菜)有多個,而且不僅在崑山種,還在上海種…
//第一種抽象產品類:Fruit
abstract class Fruit{}
//Fruit的具體產品類
class Apple extends Fruit{}
class Potato extends Fruit{}
//第二種抽象產品類:Vegetable
abstract class Vegetable{}
//Vegetable的具體產品類
class Tomato extends Vegetable{}
class Tomato extends Vegetable{}
//抽象工廠類(一個產品族的抽象)
public abstract class AbstractFactory {
public abstract Fruit getFruit();
public abstract Vegetable getVegetable();
}
//具體工廠類:KunShanFactory(崑山工廠種植蘋果和西紅柿)
class KunShanFactory extends AbstractFactory{
@Override
public Fruit getFruit() {
return new Apple();
}
@Override
public Vegetable getVegetable() {
return new Tomato();
}
}
//具體工廠類:ShaiHai(上海工廠種植香蕉和土豆)
class ShaiHaiFactory Factory extends AbstractFactory{
@Override
public Fruit getFruit() {
return new Banana();
}
@Override
public Vegetable getVegetable() {
return new Potato();
}
}
- 優點 & 缺點
- 優點
- 解決多維度產品的生產問題。(工廠模式針對的是一個產品等級結構,抽象工廠模式針對的是面向多個產品等級結構)
- 具有工廠方法模式(降低了對象之間的耦合度)的優點。
- 缺點
- 產品族的擴展十分費力。產品族中需要增加一個新的產品,則幾乎所有的工廠類都需要進行修改。
- 在具體工廠類的方法中,對於產品族裏的產品,只能使用其中一個。(崑山基地只能種水果類中的蘋果,不能種其他的)。
- 舉例:這個也比較好理解。在一些場景中,很適用:如一款車(具體工廠類)中具有海爾空調(具體產品1)和國產發動機(具體產品2),而對於空調來說,同一款車子不可以搭載兩種空調。
- 優點
1.2 單例模式
- 核心:確保一個類只能有一個實例,且該類自己創建自己供外部用戶使用
- 兩種單例模式
-
餓漢式單例類:單例類被加載時,其靜態變量被初始化,同時私有構造器被調用。
public class Singleton { //以下兩行任意選一行(兩種方法) private static Singleton s = new Singleton(); //public final static Singleton s = new Singleton(); private Singleton(){} public static Singleton getSingleton(){ return s; } }
-
懶漢式單例類:單例類被加載時不會被實例化,等到有人請求實例的時候根據情況構建。(控制併發訪問)
public class Singleton { private static Singleton s; private Singleton(){} synchronized public static Singleton getSingleton(){ if(s==null){ s = new Singleton(); } return s; } }
-
1.3 生成器模式(Builder)
- 概述
- 一個產品通常由多個零部件組成,構建過程比較複雜。將產品的實現細節和產品的表現相分離。
- 注意:是解耦"構建對象過程"(蓋房子的過程)和"對象的部件"(房子由窗戶、門、牆構成)
- 示例代碼:蓋房子
//房子的組件類:牆、窗戶、門
class Wall{}
class Window{}
class Door{}
//房子類
class House{
private String Wall;
private String Window;
private String Door;
}
//抽象類:民工
abstract class Builder{
House house = new House(); //構建房子的組成部件
abstract buildWall();
abstract buildWindow();
abstract buildDoor();
House getHouse(){return house;};
}
//民工的實現類:蓋草房的民工
class CaoBuilder extends Builder{
@Override
public void buildWall() {
}
@Override
public void buildWindow() {
}
@Override
public void buildDoor() {
}
}
//設計師:蓋房子的過程
Designer{
Builder builder;
Public Designer(Builder builder){
this.builder = builder;
}
constructHouse(){
builder.buildWall();
builder.buildWindow();
builder.buildDoor();
}
}
Client{
Builder builder = new caoBuilder();
Designer designer = new Designer(builder);
public static void main(String[] args) {
designer.constructHouse();
builder.getHouse();
}
}
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BSwg33e5-1573197074289)(E:/myYouDaoYun/imageFile/OOAD_Builder.png)]
2. 結構型模式
2.1 適配器模式
- 適配器模式使得原本由於接口不兼容而不能一起工作的那些類可以在一起工作。
- 示例(前提:兩個適配器)
//A:香港充電器(只能充香港手機) B:內陸充電器(只能充內陸手機)
interface InterfaceA{ //適配器類
MethodA();
}
interface InterfaceB{ //適配器類
methodB();
}
- 示例代碼:拿一部國產手機去香港,需要用香港的充電器,A代表香港的充電器,b相當於適配器(即在A上插入一個插頭,這個插頭可以讓國產手機充電)
class C implements A{ //只能由A適配到B(單向適配)
InterfaceB b; //相當於適配器
methodA(){
b.methodB();
}
}
Class C implements A,B{ //雙向適配
InterfaceB b;
InterfaceA a;
methodA(){
b.methodB();
}
methodB(){
a.methodA();
}
}
2.2 Façade模式(外觀模式/門面模式)
- 概述
- Facade模式:爲子系統中的一組接口提供一致的界面,Façade模式定義了一個高層接口,這個接口使得這一子系統更加容易使用.
- A爲了完成某項功能,需要和一系列的對象進行交互,這個時候可以提取出一個門面類,由門面類和那些對象進行交互,而讓A與這個門面類進行交互(最少交互原則,迪米特法則)。
- 示例代碼
/*Facade模式(門面模式):將所有的對象組合起來,一起啓動和關閉
* 此程序中:Facade類將AlarmA和Monitor類關聯起來,一起控制它們的啓動和關閉
* */
//報警器類
class AlarmA{
private String name;
public AlarmA(String name) {
this.name = name;
}
public void turnOn(){
System.out.println(name+"報警器啓動");
}
public void turnOff(){
System.out.println(name+"報警器關閉");
}
}
//監控器類
class Monitor{
private String name;
public Monitor(String name) {
this.name = name;
}
public void turnOn(){
System.out.println(name+"監控器啓動");
}
public void turnOff(){
System.out.println(name+"監控器關閉");
}
}
//門面類
public class Facade {
private AlarmA a;
private Monitor b;
public Facade(AlarmA a,Monitor b) {
this.a = a;
this.b =b;
}
public void turnOn(){
a.turnOn();
b.turnOn();
}
public void turnOff(){
a.turnOff();
b.turnOff();
}
public static void main(String[] args) {
AlarmA a = new AlarmA("A");
Monitor b = new Monitor("B");
Facade f= new Facade(a,b);
f.turnOn(); //一起控制報警器和監控器的啓動
System.out.println("---------");
f.turnOff(); //一起控制報警器和監控器的關閉
}
}
2.3 代理模式
- 概述:爲其他對象提供一種代理以控制對這個對象的訪問。
- 注意:代理需要有被代理的行爲,而且要比被代理的更豐富,還需要有過濾功能。
- 示例:老闆和祕書
3. 行爲型模式
3.1 Command命令模式
- Command命令模式:對命令進行封裝,把發出命令的職責和執行命令的職責分隔開.
- 角色可以分爲:
- 請求者角色:負責調用命令對象
- 接受者角色:命令的接受者,負責具體實施命令。
- 命令角色
- 示例:我叫張三去買水
- 分析
- 請求者/發出者:我
- 接受者:張三
- 命令:買水
- 示例代碼
//命令(抽象類) public interface Command { void execute(); } //具體命令 class BuyWater implements Command{ private Receiver receiver; public BuyWater(Receiver receiver) { this.receiver = receiver; } @Override public void execute() { //命令關聯執行者 receiver.action("去買水"); } } //請求者/發出者 class Invoker{ //發出者發送命令 public void send(Command command){ command.execute(); } } //接受者 class Receiver { private String name; public Receiver(String name) { this.name = name; } public void action(String msg){ System.out.println("我是"+name); System.out.println("我準備"+msg); } } //客戶端 public class Client { public static void main(String[] args) { //命令 //命令的執行者 //命令的發出者 //李四讓張三去買水 Receiver r = new Receiver("張三"); Command com = new BuyWater(r); Invoker in = new Invoker(); in.send(com); } }
- 分析
3.2 觀察者模式(Observer)
-
概述:一個軟件系統中包含了多個對象,對象之間彼此依賴和約束。通常一個對象的狀態發生改變時,會引起其他的對象做出相應的改變.觀察者模式就是其中一種方案,當被觀察者在狀態發生改變時,主動通知所有對他感興趣的觀察者對象,使觀察者對象做出相應的改變。
-
觀察者模式:定義了一個一對多的依賴關係,讓一個或多個觀察者對象監察一個主題對象。這樣一個主題對象在狀態上的變化能夠通知所有依賴於此對象的觀察者對象,使這些觀察者對象能夠自動更新。
-
角色
- 被觀察者:一個
- 觀察者:多個
- 主題/事件:被觀察者可以察覺到的事件,或被觀察者的狀態。
-
觀察者模式的總結:一個被觀察者對應多個觀察者,當被觀察者在狀態發生改變時,主動通知所有對他感興趣的觀察者對象,使觀察者對象做出相應的改變。
-
觀察者模式的應用:事件監聽處理機制
-
示例
- 題目:小明和弟弟對媽媽說:“媽媽,我們在院子裏玩,飯做好了就叫我們一聲”。請用觀察者模式加以解釋。
- 觀察者:小明和弟弟
- 被觀察者:媽媽
- 分析:”小明”和”弟弟”向系統的主題“媽媽”登記了一個感興趣的事件(“飯熟了”),媽媽在事件發生時,通知所有對該事件感興趣的觀察者對象(也就是“小明”和“弟弟”),使他們改變原有行爲採取相應的行爲(“去喫飯”).
- 題目:小明和弟弟對媽媽說:“媽媽,我們在院子裏玩,飯做好了就叫我們一聲”。請用觀察者模式加以解釋。
-
示例代碼
- 觀察者
public interface Observer { //觀察者:小明和弟弟
void listener(); //一直監聽媽媽發來的通知
}
class ObserverInstance implements Observer{
private String name; //觀察者的名字
public ObserverInstance(String name) {
this.name = name;
}
@Override
public void listener() {
System.out.println(name+"收到通知!");
}
}
- 被觀察者:當主題(飯熟了)變化時,他主動通知對她感興趣的觀察者
public interface BeiObserver { //被觀察者:媽媽
void addObserver(Observer observer);
void removeAllObsever();
void notifyAllObserver();
}
class BeiOberserInstance implements BeiObserver{
List<Observer> list = new ArrayList<Observer>();
public void setName(String name) { //設置飯的狀態
notifyAllObserver();
}
@Override
public void addObserver(Observer observer) {
list.add(observer);
}
@Override
public void removeAllObsever() {
list.removeAll(list);
}
@Override
public void notifyAllObserver() {
for(Observer observer:list){
observer.listener();
}
}
}
- 客戶端測試
public class Test {
public static void main(String[] args) {
Observer observer1 = new ObserverInstance("xiaoming");
Observer observer2 = new ObserverInstance("didi");
BeiOberserInstance beiobserver = new BeiOberserInstance();
beiobserver.addObserver(observer1);
beiobserver.addObserver(observer2);
beiobserver.setName("飯好啦~");
}
}
3.3 責任鏈模式
- 責任鏈模式:使多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係.將這些對象連成一條鏈,並連着這條鏈傳遞該請求,直到有一個對象處理它爲止。
- 舉例:網上對於一些敏感信息的過濾
SB ---> ** :) ---> O(∩_∩)O哈哈~
- 示例代碼
- 法1(通過第一個節點一直往後)
public abstract class Filter {
protected Filter nextFilter;
public void setNextFilter(Filter nextfilter){
this.nextFilter = nextfilter;
}
public abstract String doFilter(String msg);
}
class HTMLFilter extends Filter{
@Override
public String doFilter(String msg) {
String msgnew = msg.replace("<", "[").replace(">", "]");
if(nextFilter!=null){
msgnew = nextFilter.doFilter(msgnew);
}
return msgnew;
}
}
class SensitiveFilter extends Filter{
@Override
public String doFilter(String msg) {
String msgnew = msg.replace("SB","**");
if(nextFilter!=null){
msgnew = nextFilter.doFilter(msgnew);
}
return msgnew;
}
}
//客戶端
public class Client {
public static void main(String[] args) {
String msg = "SB,<script>---</script>";
Filter filter1 = new HTMLFilter();
Filter filter2 = new SensitiveFilter();
filter1.setNextFilter(filter2);
String result = filter1.doFilter(msg); //通過依賴第一個節點完成後續節點的過濾
System.out.println(result);
}
}
- 法2:使用FilterChain:將所有的Filter示例都加入FilterChain中
public interface Filter {
public String doFilter(String msg, FilterChain chain);
}
class FilterChain implements Filter { //責任鏈
LinkedList<Filter> list;
int index = 0;
private int i = 0;
public FilterChain() {
list = new LinkedList<Filter>();
}
public void addFilter(Filter filter){
list.add(filter);
}
@Override
public String doFilter(String msg, FilterChain chain) {
if(i==list.size()) return msg; //這是最後一個過濾器
Filter f = list.get(i); //取第幾個過濾器
i++;
return f.doFilter(msg, chain);
}
}
class HTMLFilter implements Filter {
@Override
public String doFilter(String msg, FilterChain chain) {
String msgnew = msg.replace("<", "[").replace(">", "]");
msgnew = chain.doFilter(msgnew, chain);
return msgnew;
}
}
class SensitiveFilter implements Filter {
@Override
public String doFilter(String msg, FilterChain chain) {
String msgnew = msg.replace("SB", "**");
msgnew = chain.doFilter(msgnew, chain);
return msgnew;
}
}
//客戶端
public class Client {
public static void main(String[] args) {
String msg = "SB,<script>----</script>";
Filter filter1 = new HTMLFilter();
Filter filter2 = new SensitiveFilter();
FilterChain chain = new FilterChain();
chain.addFilter(filter1);
chain.addFilter(filter2);
String result = chain.doFilter(msg, chain); //通過責任鏈來完成每個節點的過濾
System.out.println(result);
}
}
3.4 狀態模式
- 狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行爲也隨之改變。
設計模式精簡版
- 創建型模式:解決對象的創建問題
- 工廠模式[簡單工廠模式,工廠方法模式,抽象工廠模式]
- 單例模式[餓漢式單例模式,懶漢式單例模式]
- 生成器模式/Builder:將"構建對象的過程"和"對象的部件"分開
- 結構型模式:描述如何將類和對象結合在一起,構成更大的結構。
- 適配器模式:使原本由於接口不兼容而不能一起工作的那些類可以一起工作
- Facade模式/門面模式/外觀模式:爲子系統中的一組接口提供一致的界面
- 代理模式:爲其他對象提供一種代理以控制對這個對象的訪問。代理需要有被代理的行爲,而且要比被代理的更豐富,還需要有過濾功能。
- 行爲型模式:描述的是算法和對象間職責的分配
- Command命令模式:對命令進行封裝,把發出命令的職責和執行命令的職責分隔開.
- 觀察者模式:當被觀察者在狀態發生改變時,主動通知所有對他感興趣的觀察者對象,使觀察者對象做出相應的改變。
- 責任鏈模式:將這些對象連成一條鏈,並連着這條鏈傳遞該請求,直到有一個對象處理它爲止.從而避免請求的發送者和接受者之間的耦合關係.
- 狀態模式:把所研究的對象的行爲包裝在不同的狀態對象裏,每一個狀態對象都屬於一個抽象狀態類的一個子類。狀態模式的意圖是讓一個對象在其內部狀態改變的時候,其行爲也隨之改變。