策略模式、观察者模式、代理模式、装饰模式 应用场景和实现
有個(gè)大神寫的很好:
參考:設(shè)計(jì)模式學(xué)習(xí)筆記(四:策略模式)
參考:設(shè)計(jì)模式學(xué)習(xí)筆記(二:觀察者模式)
參考:設(shè)計(jì)模式學(xué)習(xí)筆記-代理模式
參考:設(shè)計(jì)模式--裝飾者模式與代理模式(重要)
參考:設(shè)計(jì)模式——代理模式與裝飾模式的異同?(重要)
參考:設(shè)計(jì)模式之裝飾模式
參考:java模式—裝飾者模式
參考:修飾者模式(裝飾者模式,Decoration)
一、策略模式:
?(1)解決場景:
某個(gè)功能有多個(gè)方案可以實(shí)現(xiàn),要達(dá)到某個(gè)目的,需要根據(jù)具體的實(shí)際情況,選擇合適的方法,
?(2)如何實(shí)現(xiàn):
分為兩個(gè)層次
(a) 環(huán)境類
環(huán)境類負(fù)責(zé)接收用戶的請求,并根據(jù)實(shí)際情況把相應(yīng)的請求委托給一組策略類中的一個(gè);
(b) 一組策略類
一組封裝了具體的實(shí)現(xiàn)類的算法,并負(fù)責(zé)具體的計(jì)算過程
(3)關(guān)鍵類圖:
?
?首先定義策略類的接口:?
public interface GameStrategy {public void goldCoin();}其次 實(shí)現(xiàn)一組策略類:
public class normalStrategy implements GameStrategy { @Override public void goldCoin() { // System.out.println("普通玩家沒有折扣"); } } public class advancedStrategy implements GameStrategy {@Overridepublic void goldCoin() {// TODO Auto-generated method stubSystem.out.println("高級會員9折優(yōu)惠");} }環(huán)境類的實(shí)現(xiàn):
public class Context { //持有一個(gè)Strategy的引用 private GameStrategy mGameStrategy; // 構(gòu)造函數(shù) public Context(GameStrategy strategy) { this.mGameStrategy = strategy; } public void setStrategy(GameStrategy strategy) { this.mGameStrategy = strategy; } public void goldCoin() { this.mGameStrategy.goldCoin(); } }最后根據(jù)不同的情況進(jìn)行調(diào)用:
public class main { public static void main(String[] args) { //普通玩家策略 Context context = new Context(new normalStrategy()); context.goldCoin(); //高級玩家策略 context.setStrategy(new advancedStrategy()); context.goldCoin(); } }?二、觀察者模式
(1)解決場景:
(a) 對一個(gè)對象狀態(tài)的更新,需要其他對象同步更新
(b) 對象僅需要將自己的更新通知給其他對象而不需要知道其他對象的細(xì)節(jié)
觀察者模式,又稱為發(fā)布訂閱模式,它的特點(diǎn):
(a) subject 和 observer之間是松耦合的,各自獨(dú)立實(shí)現(xiàn),
(b) subject在發(fā)送廣播通知的時(shí)候,不需要指定具體的observer,observer可以自行決定是否要訂閱
(c) 遵循常用設(shè)計(jì)原則:高內(nèi)聚,低耦合
(2)如何實(shí)現(xiàn):
關(guān)鍵類圖:
?定義觀察者接口:?
public interface Observer {void update(String message,String name);}實(shí)現(xiàn)觀察者:
public class Bianyi1 implements Observer { //定義姓名 private String bname = "張昊天"; @Override public void update(String message,String name) { System.out.println(bname+":"+name+"那里有新情況:"+ message); } } public class Bianyi2 implements Observer { //定義姓名 private String bname = "石破天"; @Override public void update(String message,String name) { System.out.println(bname+":"+name+"那里有新情況:"+ message); } }定義目標(biāo)接口:
public interface Huairen { //添加便衣觀察者 void addObserver(Observer observer); //移除便衣觀察者 void removeObserver(Observer observer); //通知觀察者 void notice(String message); }兩個(gè)實(shí)現(xiàn)目標(biāo)的類:
import java.util.*; /** * 嫌犯大熊 */ public class XianFan1 implements Huairen { //別稱 private String name = "大熊"; //定義觀察者集合 private List<Observer> observerList = new ArrayList<Observer>(); //增加觀察者 @Override public void addObserver(Observer observer) { if(!observerList.contains(observer)){ observerList.add(observer); } } //移除觀察者 @Override public void removeObserver(Observer observer) { if(observerList.contains(observer)){ observerList.remove(observer); } } //通知觀察者 @Override public void notice(String message) { for(Observer observer:observerList){ observer.update(message,name); } } } import java.util.*; /** * 嫌犯黑狗 */ public class XianFan2 implements Huairen { //別稱 private String name = "黑狗"; //定義觀察者集合 private List<Observer> observerList = new ArrayList<Observer>(); //增加觀察者 @Override public void addObserver(Observer observer) { if(!observerList.contains(observer)){ observerList.add(observer); } } //移除觀察者 @Override public void removeObserver(Observer observer) { if(observerList.contains(observer)){ observerList.remove(observer); } } //通知觀察者 @Override public void notice(String message) { for(Observer observer:observerList){ observer.update(message,name); } } }觀察者根據(jù)目標(biāo)進(jìn)行調(diào)用:
public class Clienter { public static void main(String[] args) { //定義兩個(gè)嫌犯 Huairen xf1 = new XianFan1(); Huairen xf2 = new XianFan2(); //定義三個(gè)觀察便衣警察 Observer o1 = new Bianyi1(); Observer o2 = new Bianyi2(); //為嫌犯增加觀察便衣 xf1.addObserver(o1); xf1.addObserver(o2); xf2.addObserver(o1); xf2.addObserver(o3); //定義嫌犯1的情況 String message1 = "又賣了一批貨"; String message2 = "老大要下來視察了"; xf1.notice(message1); xf2.notice(message2); } }?
三、代理模式:
(1)解決場景:
? ? (a) 當(dāng)我們想要隱藏某個(gè)類時(shí),可以為其提供代理類
(b) 當(dāng)一個(gè)類需要對不同的調(diào)用者提供不同的調(diào)用權(quán)限時(shí),可以使用代理類來實(shí)現(xiàn)
(c) 當(dāng)我們要擴(kuò)展某個(gè)類的某個(gè)功能時(shí),可以使用代理模式,在代理類中進(jìn)行簡單擴(kuò)展
(2) 如何實(shí)現(xiàn):
(a) 代理類與委托類實(shí)現(xiàn)同一接口
(b) 在委托類中實(shí)現(xiàn)功能,在代理類的方法中中引用委托類的同名方法
(c) 外部類調(diào)用委托類某個(gè)方法時(shí),直接以接口指向代理類的實(shí)例,這正是代理的意義所在:屏蔽。
(3) 類圖:
?(a) 抽象的主題:
public interface Moveable {void move() throws Exception; }(b)真實(shí)主題:
public class Car implements Moveable {public void move() throws Exception {Thread.sleep(new Random().nextInt(1000));System.out.println("汽車行駛中…");} }(c) 事務(wù)處理器:
public class TimeHandler implements InvocationHandler {private Object target;public TimeHandler(Object target) {super();this.target = target;}/*** 參數(shù):*proxy 被代理的對象*method 被代理對象的方法*args 方法的參數(shù)* 返回:*Object 方法返回值*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {long startTime = System.currentTimeMillis();System.out.println("汽車開始行駛…");method.invoke(target, args);long stopTime = System.currentTimeMillis();System.out.println("汽車結(jié)束行駛…汽車行駛時(shí)間:" + (stopTime - startTime) + "毫秒!");return null;}}(d) 調(diào)用:
public class Test {public static void main(String[] args) throws Exception{Car car = new Car();InvocationHandler h = new TimeHandler(car);Class<?> cls = car.getClass();/***loader 類加載器*interfaces 實(shí)現(xiàn)接口*h InvocationHandler*/Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);m.move();}?四、裝飾模式:
裝飾者(decorator)模式:在不改變對象自身的基礎(chǔ)上,在程序運(yùn)行期間給對像動態(tài)的添加職責(zé)。與繼承相比,裝飾者是一種更輕便靈活的做法。
裝飾者模式的特點(diǎn)
可以動態(tài)的給某個(gè)對象添加額外的職責(zé),而不會影響從這個(gè)類中派生的其它對象;
(1)應(yīng)用場景:
(a)在不影響其他對象的情況下,以動態(tài)、透明的方式給單個(gè)對象添加職責(zé)。
? (b)? 需要?jiǎng)討B(tài)地給一個(gè)對象增加功能,這些功能也可以動態(tài)地被撤銷。? 當(dāng)不能采用繼承的方式對系統(tǒng)進(jìn)行擴(kuò)充或者采用繼承不利于系統(tǒng)擴(kuò)展和維護(hù)時(shí)。
(2)實(shí)現(xiàn)的例子:
最常見的就是輸入輸出流:
BufferedReader in1 = new BufferedReader(new InputStreamReader(new FileInputStream(file)));//字符流DataInputStream in2 = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));//字節(jié)流// DataInputStream-從數(shù)據(jù)流讀取字節(jié),并將它們裝換為正確的基本類型值或字符串// BufferedInputStream-可以通過減少讀寫次數(shù)來提高輸入和輸出的速度?
從例子開始講解,比如今天你要出門約會,你肯定是要決定好你要穿什么樣的衣服出門,用衣服來裝飾下自己,讓自己擁有一個(gè)完美的約會。比如,你會穿一件襯衫,然后穿一件西服褲,最后穿皮鞋出門。這就是裝飾者模式的一個(gè)例子。為了實(shí)現(xiàn)這個(gè)功能,會有下面的代碼。 public class People {public void wearShirt(){System.out.println("******穿襯衫******");} public void wearTrouser(){System.out.println("******穿西服褲******");}public void wearShoes(){System.out.println("******穿皮鞋******");}}?
? public class Client { public static void main(String[] args) {People people = new People();people.wearShirt();people.wearTrouser();people.wearShoes();} }?
雖然上面的代碼實(shí)現(xiàn)了出門穿衣服的功能,但是這里會問題。如果我現(xiàn)在要增加一個(gè)功能,我要先穿襪子,再穿皮鞋。此時(shí)就要在People類中增加一個(gè)穿襪子的方法。這就違背了設(shè)計(jì)模式的開閉原則,所謂開閉原則就是類可以進(jìn)行擴(kuò)展,但是不可以進(jìn)行修改。所以就有如下的設(shè)計(jì): public interface People { public void wearClothing(); } public class Xiaoming implements People{ private String name; public Xiaoming(){name = "小明";} public void wearClothing(){System.out.println(name+"******開始穿衣服******");}public String getName() {return name;}} public abstract class Finery implements People { protected People people; public Finery(People people){this.people = people;}public abstract void wearClothing();} public class ShirtFinery extends Finery {public ShirtFinery(People people){super(people);}@Overridepublic void wearClothing() {people.wearClothing();System.out.println("******穿襯衫******");} } public class ShoesFinery extends Finery { public ShoesFinery(People people){super(people);}@Overridepublic void wearClothing() {people.wearClothing();System.out.println("******穿皮鞋*******");} }public class TrouserFinery extends Finery {public TrouserFinery(People people){super(people);}@Overridepublic void wearClothing() {people.wearClothing();System.out.println("******穿西服褲*******");} } public class Client { public static void main(String[] args) {People people = new Xiaoming();Finery shirtFinery = new ShirtFinery(people);Finery trouserFinery = new TrouserFinery(shirtFinery);Finery shoesFinery = new ShoesFinery(trouserFinery);shoesFinery.wearClothing();}}? (3)類圖:
People是定義了一個(gè)接口,用來添加具體的職責(zé),而Xiaoming是具體的People,也就是被裝飾的對象。對于Finery實(shí)現(xiàn)了People接口,進(jìn)而對People進(jìn)行擴(kuò)展,而Finery的子類就是具體的裝飾類,Finery中依賴People類,裝飾的具體對象。 這就是所謂的裝飾者模式。如果我要添加穿襪子的步驟,則只需要再添加一個(gè)實(shí)現(xiàn)類,完全不需要修改其他代碼(Client是客戶端類,肯定是要修改的)。
?
裝飾者模式和代理模式的區(qū)別:
兩種模式的特點(diǎn)
裝飾模式:
在不改變接口的前提下,動態(tài)擴(kuò)展對象的訪問。
動態(tài)繼承,讓類具有在運(yùn)行期改變行為的能力。
裝飾模式,突出的是運(yùn)行期增加行為,這和繼承是不同的,繼承是在編譯期增加行為。
強(qiáng)調(diào):增強(qiáng),新增行為
代理模式:
在不改變接口的前提下,控制對象的訪問。
1.從封裝的角度講,是為了解決類與類之間相互調(diào)用而由此導(dǎo)致的耦合關(guān)系,可以說是接口的另外一個(gè)層引用。
比如:在a類->b代理->c類這個(gè)關(guān)系中,c類的一切行為都隱藏在b中。即調(diào)用者不知道要訪問的內(nèi)容與代理了什么對象。
2.從復(fù)用的角度講,可以解決不同類調(diào)用一個(gè)復(fù)雜類時(shí),僅僅因較小的改變而導(dǎo)致整個(gè)復(fù)雜類新建一個(gè)類。
比如:a類->c類1;b類->c類2。
可以變?yōu)閍類->ca代理類->c類;b類->cb代理類-c類。
代理模式,是類之間的封裝和(某方面的)復(fù)用。
強(qiáng)調(diào):限制,控制訪問
?
總結(jié):
1. 裝飾模式可以讓使用者直觀的看到增強(qiáng)了哪些功能,而代理模式完全限制了使用者。
2. 對裝飾模式來說,裝飾者(Decorator)和被裝飾者(Cafe)都實(shí)現(xiàn)同一個(gè) 接口。
3. 對代理模式來說,代理類(Proxy Class)和真實(shí)處理的類(Real Class)都實(shí)現(xiàn)同一個(gè)接口。
4.?此外,不論我們使用哪一個(gè)模式,都可以很容易地在真實(shí)對象的方法前面或者后面加上自定義的方法。
裝飾模式與繼承的比較
明顯的,裝飾模式可以動態(tài)的擴(kuò)展對象的行為。
比如某對象有30項(xiàng)行為,但是在第一階段用到1-20行為,第二階段用到11-30項(xiàng)行為,所以這時(shí)候,就可以只定義11-20的行為。
在第一階段運(yùn)行時(shí),可以將1-10的行為以“裝飾1”給加上
到第二階段運(yùn)行時(shí),可以將“裝飾1”去掉,將21-30的能以“裝飾2”給加上。
但是繼承是在編譯期增加行為。
裝飾模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1. 裝飾模式可以提供比繼承更多地靈活性。
2. 可以通過一種動態(tài)的方式來擴(kuò)展一個(gè)對象的功能,在運(yùn)行時(shí)選擇不同的裝飾器,從而實(shí)現(xiàn)不同的行為。
3. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,可以創(chuàng)造出很多不同行為的組合。可以使用多個(gè)具體裝飾類來裝飾同一對象,得到功能更為強(qiáng)大的對象。
4. 具體構(gòu)件類與具體裝飾類可以獨(dú)立變化,用戶可以根據(jù)需要增加新的具體構(gòu)件類和具體裝飾類,在使用時(shí)再對其進(jìn)行組合,原有代碼無須改變,符合“開閉原則”。
缺點(diǎn):
1. 會產(chǎn)生很多的小對象(具體裝飾類),增加了系統(tǒng)的復(fù)雜性。
2. 這種比繼承更加靈活機(jī)動的特性,也同時(shí)意味著裝飾模式比繼承易于出錯(cuò),排錯(cuò)也很困難,對于多次裝飾的對象,調(diào)試時(shí)尋找錯(cuò)誤可能需要逐級排查,較為煩瑣。
總結(jié)
以上是生活随笔為你收集整理的策略模式、观察者模式、代理模式、装饰模式 应用场景和实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ehviewer苹果版下载_苹果用户:支
- 下一篇: sqlserver中文显示问号_解决 S