設(shè)計(jì)模式是一套面向?qū)ο蟮拇a設(shè)計(jì)經(jīng)驗(yàn)總結(jié),是在編程領(lǐng)域被反復(fù)使用、多人知曉的通過了分類整理的代碼設(shè)計(jì)方法。
使用設(shè)計(jì)模式為了可重用代碼,使代碼更加容易理解、保證代碼的可靠性。合理的設(shè)計(jì)模式對(duì)開發(fā)人員和需要開發(fā)的系統(tǒng)都是有利的,設(shè)計(jì)模式可以讓代碼實(shí)現(xiàn)工程化。
設(shè)計(jì)原則
1. 單一職責(zé)原則
定義:對(duì)于一個(gè)類,應(yīng)該只有一個(gè)引起它變化的原因。
好處: 降低類的復(fù)雜度、提高類的可讀性、提高系統(tǒng)的可維護(hù)性。
2. 里氏代換原則
定義:子類型必須能夠替換掉父類型
優(yōu)點(diǎn):代碼共享、提高了代碼重用性、擴(kuò)展性、產(chǎn)品和項(xiàng)目的開放性。
只有滿足里氏代換原則,子類可以代替父類出現(xiàn)并且不影響層序的功能父類才能得到真正的復(fù)用,并且子類可以在弗雷德基礎(chǔ)上添加新的行為。面向?qū)ο笏枷胫?,父類和子類的繼承關(guān)系是抽象化的具體實(shí)現(xiàn)。
里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。
3. 開放封閉原則
定義:程序的實(shí)體對(duì)象(模塊、類、函數(shù)等)應(yīng)該可以進(jìn)行擴(kuò)展,但不應(yīng)該修改。
優(yōu)點(diǎn):可擴(kuò)展性好、耦合度低。
特征
- 對(duì)于擴(kuò)展是開放的
- 對(duì)于修改是封閉的
// 未實(shí)現(xiàn)開放封閉原則
public class BankWorker {
public void saving() {
System.out.println("存款");
}
public void drawing() {
System.out.println("取款");
}
}
// 實(shí)現(xiàn)開放封閉原則
public interface BankWorker {
public void operation();
}
public class SavingBankWorker extends BankWorker {
@Override
public void operation() {
System.out.println("存款");
}
}
public class DrawingBankWorker extends BankWorker {
@Override
public void operation() {
System.out.println("取款");
}
}
開放封閉原則可以通過擴(kuò)展已有的軟件系統(tǒng)提供新的行為,以滿足軟件的新需求,使變化的軟件有一定的靈活性和適應(yīng)性。對(duì)于已有的軟件模塊,特別是最重要的抽象模塊不能再修改,這就是軟件系統(tǒng)有了一定的穩(wěn)定性和延續(xù)性。
4. 依賴倒轉(zhuǎn)原則
定義:程序高層模塊不應(yīng)該依賴于低層模塊,但兩者都應(yīng)該依賴與抽象,抽象不應(yīng)該依賴于具體細(xì)節(jié),而細(xì)節(jié)應(yīng)該依賴與抽象。
優(yōu)點(diǎn):可擴(kuò)展性好、耦合度低。
interface IWorker {
public void work();
}
class Work implements IWorker {
@Override
public void work() {
//......working
}
}
class SeniorWorker implements IWorker {
@Override
public void work() {
//......working much more
}
}
class Manager {
IWorker mWorker;
public void setWorker(IWorker w) {
mWorker = w;
}
public void manage() {
mWorker.work();
}
}
5. 合成/聚合復(fù)用原則
定義:盡量不實(shí)用類繼承、盡量使用合成/聚合。
優(yōu)點(diǎn):降低耦合度、提升代碼可讀性、隱藏實(shí)現(xiàn)細(xì)節(jié)。
缺點(diǎn):系統(tǒng)中有較多對(duì)象需要管理。
- 將軍對(duì)自己的統(tǒng)領(lǐng)將士保持有引用,就是聚合。
- 皇帝擁有天下,但將軍并非擁有全部士兵,所以對(duì)將軍“擁有”的關(guān)系,相當(dāng)于合成,大將軍是這個(gè)合成中的一個(gè)對(duì)象。
6. 迪米特法則
定義:如果兩個(gè)類之間不必進(jìn)行直接通信,則這兩個(gè)類不應(yīng)該發(fā)生直接的相互作用。如果一個(gè)類需要調(diào)用另一個(gè)類的某個(gè)方法,則可以通過第三方來(lái)轉(zhuǎn)發(fā)這個(gè)調(diào)用。
// 皇帝找一個(gè)侍衛(wèi),皇帝這么傲嬌,一定不會(huì)親自動(dòng)手啊,所以要通過太監(jiān)
public abstract class Stranger {
public abstract void operation();
}
public class ShiWei extends Stranger {
@Override
public void operation() {
System.out.println("參見皇上,我是侍衛(wèi)啊哈哈哈!");
}
}
public class TaiJian extends Stranger {
@Override
public void operation() {
}
// 太監(jiān)找人啊哈哈
public void findStranger() {
Stranger s = new ShiWei();
s.operation();
}
}
public class HuangDi {
private TaiJian t;
public TaiJian getTaiJian() {
return t;
}
public void setTaiJian(TaiJian t) {
this.t = t;
}
public void operation() {
}
}
public class Test {
public static void main(String[] args){
HuangDi HD = new HuangDi();
// 找個(gè)太監(jiān),小桌子小凳子啊哈哈
HD.setTaiJian(new TaiJian());
// 來(lái),小桌子,去給我把他找來(lái)(這個(gè)過程中皇帝并沒有和侍衛(wèi)直接接觸)
HD.getTaiJian().findStranger();
}
}
Java設(shè)計(jì)模式
Java中一般有23種設(shè)計(jì)模式,這里只介紹一些常用的~
總體來(lái)說設(shè)計(jì)模式分為三大類:
創(chuàng)建型模式,共5種
工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。結(jié)構(gòu)型模式,共7種
適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。行為型模式,共11種
策略模式、模板方法模式、觀察者模式、迭代子模式、責(zé)任鏈模式、命令模式、備忘錄模式、狀態(tài)模式、訪問者模式、中介者模式、解釋器模式。
1. 單例設(shè)計(jì)模式
定義:保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問它的全局訪問點(diǎn)。
適用:
- 當(dāng)一個(gè)類只能有一個(gè)實(shí)例而卻第三方可以從一個(gè)公共的訪問點(diǎn)訪問它時(shí)。
- 當(dāng)一個(gè)唯一的實(shí)力可以通過子類來(lái)擴(kuò)展,而且第三方需要在不更改代碼的情況下就能使用一個(gè)擴(kuò)展的實(shí)例時(shí)。
- 餓漢式
public class Singleton {
// 直接創(chuàng)建對(duì)象
public static Singleton instance = new Singleton();
// 私有化構(gòu)造函數(shù)
private Singleton() {
}
// 返回對(duì)象實(shí)例
public static Singleton getInstance() {
return instance;
}
}
這種方式比較常用,但容易產(chǎn)生垃圾對(duì)象。
優(yōu)點(diǎn):沒有加鎖,執(zhí)行效率會(huì)提高。
缺點(diǎn):類加載時(shí)就初始化,浪費(fèi)內(nèi)存。
它基于 Classloader機(jī)制避免了多線程的同步問題,不過,instance 在類裝載時(shí)就實(shí)例化,雖然導(dǎo)致類裝載的原因有很多種,在單例模式中大多數(shù)都是調(diào)用getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態(tài)方法,比如反射)導(dǎo)致類裝載,這時(shí)候初始化 instance 顯然沒有達(dá)到 lazy loading 的效果。
2、懶漢模式(線程安全)
public class Singletion {
private static Singleton instance;
private Singleton () {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 為了處理并發(fā),每次調(diào)用getInstance方法時(shí)都需要進(jìn)行同步,會(huì)有不必要的同步開銷。
3、雙重檢查模式(DCL)
public class Singleton {
private static volatile Singleton instance;
private Singleton {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
- 第一次判空,省去了不必要的同步。第二次是在Singleton等于空時(shí)才創(chuàng)建實(shí)例。
- 使用volatile保證了實(shí)例的可見性。
- DCL在一定程度上解決了資源的消耗和多余的同步、線程安全等問題,但是在某些情況下會(huì)失效。
假設(shè)線程A執(zhí)行到instance = new Singleton()語(yǔ)句,看起來(lái)只有一行代碼,但實(shí)際上它并不是原子操作,這句代碼最終會(huì)被編譯成多條匯編指令,它大致做了3件事:
1)給instance的實(shí)例分配內(nèi)存。
2)調(diào)用Singleton()構(gòu)造函數(shù),初始化成員字段。
3)將instance對(duì)象指向分配的內(nèi)存空間(此時(shí)instance就不是null了)。
原子操作:不需要synchronized,指不會(huì)被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開始,就一直運(yùn)行到結(jié)束,不會(huì)切換到另一個(gè)線程。
eg:A-B銀行轉(zhuǎn)賬1000
但是,由于Java編譯器允許處理器亂序執(zhí)行,以及JDK1.5之前JMM中的Cache、寄存器到主內(nèi)存回寫順序的規(guī)定,上面的2和3的順序是無(wú)法保證的,也就是說,執(zhí)行順序可能是1-2-3也可能是1-3-2。如果是后者,并且在3執(zhí)行完畢、2未執(zhí)行之前,被切換到線程B上,這時(shí)候instance因?yàn)橐呀?jīng)在線程A內(nèi)執(zhí)行過了3,instance已經(jīng)是非空了,所以,線程B直接取走instance,再使用時(shí)就會(huì)出錯(cuò),這就是DCL失效問題,而且這種難以跟蹤難以重現(xiàn)的錯(cuò)誤可能會(huì)隱藏很久。
在JDK1.5之后,SUN官方已經(jīng)注意到這種問題,調(diào)整了JVM,具體化了volatile關(guān)鍵字,因此,如果JDK1.5或之后的版本,只需要將instance的定義改成private volatile static Singleton instance = null就可以保證instance對(duì)象每次都是從主內(nèi)存中讀取,就可以使用DCL的寫法來(lái)完成單例模式。當(dāng)然,volatile或多或少也會(huì)影響到性能,但考慮到程序的正確性,這點(diǎn)犧牲也是值得的。
volatile:當(dāng)一個(gè)共享變量被volatile修飾時(shí),它會(huì)保證修改的值會(huì)立即被更新到主存,當(dāng)有其他線程需要讀取時(shí),它會(huì)去內(nèi)存中讀取新值。來(lái)保證可見性
DCL優(yōu)點(diǎn):資源利用率高,第一次執(zhí)行g(shù)etInstance時(shí)單例對(duì)象才會(huì)被實(shí)例化,效率高。
缺點(diǎn):第一次加載稍慢,也由于JMM的原因?qū)е屡紶枙?huì)失敗。在高并發(fā)環(huán)境下也有一定的缺陷,雖然發(fā)生概率很小。DCL模式是使用最多的單例實(shí)現(xiàn)方式,它能夠在需要時(shí)才實(shí)例化對(duì)象,并且能在絕大多數(shù)場(chǎng)景下保證對(duì)象的唯一性,除非你的代碼在并發(fā)場(chǎng)景比較復(fù)雜或低于JDK1.6版本下使用,否則,這種方式一般能夠滿足要求。
4、靜態(tài)內(nèi)部類單例模式
public class Singleton() {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final Singleton sInstance = new Singleton();
}
}
- 第一次調(diào)用getInstance方法時(shí)虛擬機(jī)才加載SingletonHolder并初始化sInstance,這樣保證了線程安全和實(shí)例的唯一性。
- (《JAVA并發(fā)編程實(shí)踐》贊成使用這種方法)
5、枚舉單例
public enum Singleton {
INSTANCE;
public void doSomeThing() {
}
}
- 默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,并且在任何情況下都是單例。
- 簡(jiǎn)單、可讀性不高。
注意:上面的幾種單例模式創(chuàng)建的單例對(duì)象被反序列化時(shí)會(huì)重新創(chuàng)建實(shí)例,可以重寫readReslove方法返回當(dāng)前的單例對(duì)象。
核心原理:將構(gòu)造函數(shù)私有化,并切通過靜態(tài)方法獲取一個(gè)唯一的實(shí)例,獲取過程中必須保證線程安全、方式反序列化導(dǎo)致重新生成實(shí)例對(duì)象。
- DCL升級(jí)版本?
public class Singleton {
// 聲明變量
private static volatile Singleton singleton = null;
// 私有構(gòu)造函數(shù)
private Singleton() {
}
// 提供對(duì)外方法
public static Singleton getInstance() {
Singleton mSingleton = singleton; //定義一個(gè)臨時(shí)變量
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
mSingleton = new Singleton();
singleton = mSingleton;
}
}
}
return mSingleton ; // 返回臨時(shí)變量
}
}
使用volatile
如果沒有 volatile 關(guān)鍵字,問題可能會(huì)出在singleton = new Singleton() 這句,用偽代碼表示:
// 分配內(nèi)存
inst = allocat();
// 賦值
singleton = inst;
// 真正執(zhí)行構(gòu)造函數(shù)
constructor(inst);
可能會(huì)由于虛擬機(jī)的優(yōu)化等導(dǎo)致賦值操作先執(zhí)行,而構(gòu)造函數(shù)還沒完成,導(dǎo)致其他線程訪問得到 singleton 變量不為null,但初始化還未完成,導(dǎo)致程序崩潰。
**定義一個(gè)臨時(shí)變量 mSingleton **
訪問 volatile 變量,需要保證一些執(zhí)行順序,所以的開銷比較大。這里定義一個(gè)臨時(shí)變量,在 sInst 不為空的時(shí)候(這是絕大部分的情況),只要在開始訪問一次 volatile 變量,返回的是臨時(shí)變量。如果沒有此臨時(shí)變量,則需要訪問兩次,而降低了效率。
2. 工廠設(shè)計(jì)模式
工廠模式分為工廠方法模式和抽象工廠模式。
- 工廠方法模式
工廠方法模式分為三種:
a. 普通工廠模式:建立一個(gè)工廠類,對(duì)實(shí)現(xiàn)了同一接口的一些類進(jìn)行實(shí)例的創(chuàng)建。
b. 多個(gè)工廠方法模式:對(duì)普通工廠方法模式的改進(jìn),在普通工廠方法模式中,如果傳遞的字符串出錯(cuò),則不能正確創(chuàng)建對(duì)象,而多個(gè)工廠方法模式是提供多個(gè)工廠方法,分別創(chuàng)建對(duì)象。
c. 靜態(tài)工廠方法模式:將上面的多個(gè)工廠方法模式里的方法置為靜態(tài)的,不需要?jiǎng)?chuàng)建實(shí)例,直接調(diào)用即可。
普通工廠模式
建立一個(gè)工廠類,對(duì)實(shí)現(xiàn)了同一接口的一些類進(jìn)行實(shí)例的創(chuàng)建。
// 普通工廠模式
public interface Sender {
public void Send();
}
public class MailSender implements Sender {
@Override
public void Send() {
System.out.println("this is mail sender!");
}
}
public class SmsSender implements Sender {
@Override
public void Send() {
System.out.println("this is sms sender!");
}
}
public class SendFactory {
public Sender produce(String type) {
if ("mail".equals(type)) {
return new MailSender();
} else if ("sms".equals(type)) {
return new SmsSender();
} else {
System.out.println("請(qǐng)輸入正確的類型!");
return null;
}
}
}
多個(gè)工廠方法模式
對(duì)普通工廠方法模式的改進(jìn),在普通工廠方法模式中,如果傳遞的字符串出錯(cuò),則不能正確創(chuàng)建對(duì)象,而多個(gè)工廠方法模式是提供多個(gè)工廠方法,分別創(chuàng)建對(duì)象。
// 多個(gè)工廠方法模式
public class SendFactory {
public Sender produceMail() {
return new MailSender();
}
public Sender produceSms() {
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.produceMail();
sender.send();
}
}
靜態(tài)工廠方法模式
將上面的多個(gè)工廠方法模式里的方法置為靜態(tài)的,不需要?jiǎng)?chuàng)建實(shí)例,直接調(diào)用即可。
// 靜態(tài)工廠方法模式
public class SendFactory {
public static Sender produceMail() {
return new MailSender();
}
public static Sender produceSms() {
return new SmsSender();
}
}
public class FactoryTest {
public static void main(String[] args) {
Sender sender = SendFactory.produceMail();
sender.send();
}
}
抽象工廠模式
工廠方法模式有一個(gè)問題就是,類的創(chuàng)建依賴工廠類,也就是說,如果想要拓展程序,必須對(duì)工廠類進(jìn)行修改,這違背了閉包原則,所以,從設(shè)計(jì)角度考慮,有一定的問題,如何解決?就用到抽象工廠模式,創(chuàng)建多個(gè)工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的代碼。
// 抽象工廠模式
public interface Provider {
public Sender produce();
}
---------------------------------------------------
public interface Sender {
public void send();
}
---------------------------------------------------
public class MailSender implements Sender {
@Override
public void send() {
System.out.println("this is mail sender!");
}
}
public class SmsSender implements Sender {
@Override
public void send() {
System.out.println("this is sms sender!");
}
}
---------------------------------------------------
public class SendSmsFactory implements Provider {
@Override
public Sender produce() {
return new SmsSender();
}
}
public class SendMailFactory implements Provider {
@Override
public Sender produce() {
return new MailSender();
}
}
---------------------------------------------------
public class Test {
public static void main(String[] args) {
Provider provider = new SendMailFactory();
Sender sender = provider.produce();
sender.send();
}
}
3. 建造者模式
將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
建造者有以下角色:
- 導(dǎo)演類:負(fù)責(zé)安排已有模塊的安裝順序,最后通知建造者開始建造。
- 建造者:抽象Builder類,用于規(guī)范產(chǎn)品的組建。
- 具體建造者:實(shí)現(xiàn)抽象Builder類的所有方法,并返回建造好的對(duì)象。
- 產(chǎn)品類。
簡(jiǎn)單示例
1、產(chǎn)品類
public class Computer {
private String mCpu;
private Stiring mMainboard;
private String mRam;
public void setmCpu(String mCpu) {
this.mCpu = mCpu;
}
public void setmMainboard(String mMainboard) {
this.mMainboard = mMainboard;
}
public void setmRam(String mRam) {
this.mRam = mRam;
}
}
2、抽象建造者
public abstract class Builder {
public abstract void buildCpu(String cpu);
public abstract void buildMainboard(String mainboard);
public abstract void buildRam(String ram);
public abstract Computer create();
}
3、具體建造者
public class MoonComputerBuilder extends Builder {
private Computer mComputer = new Computer();
@Override
public void buildCpu(String cpu) {
mComputer.setmCpu(cpu);
}
@Override
public void buildMainboard(String mainboard) {
mComputer.setmMainboard(mainboard);
}
@Override
public void buildRam(String ram) {
mComputer.setmRam(ram);
}
@Override
public Computer create() {
return mComputer;
}
}
4、導(dǎo)演類
public class Director {
Builder mBuilder = null;
public Director (Builder builder) {
this.mBuilder = builder;
}
public Computer createComputer(String cpu, String mainboard, String ram) {
this.mBuilder.buildCpu(cpu);
this.mBuilder.buildMainboard(mainboard);
this.mBuilder.buildRam(ram);
return mBuilder.create();
}
}
- 屏蔽產(chǎn)品內(nèi)部組成細(xì)節(jié)。
- 具體建造者類之間相互獨(dú)立,容易擴(kuò)展。
- 會(huì)產(chǎn)生多余的建造者對(duì)象和導(dǎo)演類。
4.適配器模式
適配器模式將某個(gè)類的接口轉(zhuǎn)換成客戶端期望的另一個(gè)接口表示,目的是消除由于接口不匹配所造成的類的兼容性問題。主要分為三類:類的適配器模式、對(duì)象的適配器模式、接口的適配器模式。
類的適配器模式
package Pattern;
interface FiveVolt {
public int getVolt5();
}
class Volt220 implements FiveVolt {
@Override
public int getVolt5() {
return 220;
}
}
/**
* 類適配器
*/
class VoltAdapter extends Volt220 implements FiveVolt {
@Override
public int getVolt5() {
return 5;
}
}
public class AdapterTest {
public static void main(String[] args) {
VoltAdapter adapter = new VoltAdapter();
System.out.println("輸出電壓:" + adapter.getVolt5());
}
}
對(duì)象的適配器模式
基本思路和類的適配器模式相同,只是將 Adapter 類作修改,這次不繼承 Source 類,而是持有 Source 類的實(shí)例,以達(dá)到解決兼容性的問題。
/**
* 對(duì)象適配器
*/
class VoltAdapter implements FiveVolt {
Volt220 volt220;
public VoltAdapter(Volt220 volt220) {
this.volt220 = volt220;
}
public Volt220 getVolt220() {
return volt220;
}
@Override
public int getVolt5() {
return 5;
}
}
public class AdapterTest {
public static void main(String[] args) {
VoltAdapter adapter = new VoltAdapter(new Volt220());
System.out.println("輸出電壓:" + adapter.getVolt5());
}
}
接口的適配器模式
接口的適配器是這樣的:有時(shí)我們寫的一個(gè)接口中有多個(gè)抽象方法,當(dāng)我們寫該接口的實(shí)現(xiàn)類時(shí),必須實(shí)現(xiàn)該接口的所有方法,這明顯有時(shí)比較浪費(fèi),因?yàn)椴⒉皇撬械姆椒ǘ际俏覀冃枰?,有時(shí)只需要某一些,此處為了解決這個(gè)問題,我們引入了接口的適配器模式,借助于一個(gè)抽象類,該抽象類實(shí)現(xiàn)了該接口,實(shí)現(xiàn)了所有的方法,而我們不和原始的接口打交道,只和該抽象類取得聯(lián)系,所以我們寫一個(gè)類,繼承該抽象類,重寫我們需要的方法就行。
5. 裝飾模式(Decorator)
裝飾模式就是給一個(gè)對(duì)象增加一些新的功能,而且是動(dòng)態(tài)的,要求裝飾對(duì)象和被裝飾對(duì)象實(shí)現(xiàn)同一個(gè)接口,裝飾對(duì)象持有被裝飾對(duì)象的實(shí)例。
public interface Sourceable {
public void method();
}
----------------------------------------------------
public class Source implements Sourceable {
@Override
public void method() {
System.out.println("the original method!");
}
}
----------------------------------------------------
public class Decorator implements Sourceable {
private Sourceable source;
public Decorator(Sourceable source) {
super();
this.source = source;
}
@Override
public void method() {
System.out.println("before decorator!");
source.method();
System.out.println("after decorator!");
}
}
----------------------------------------------------
public class DecoratorTest {
public static void main(String[] args) {
Sourceable source = new Source();
Sourceable obj = new Decorator(source);
obj.method();
}
}
- 使用組合,動(dòng)態(tài)地?cái)U(kuò)展對(duì)象的功能,在運(yùn)行時(shí)能夠使用不同的裝飾器實(shí)現(xiàn)不同的行為。
- 比繼承更易出錯(cuò),旨在必要時(shí)使用。
6. 策略模式(strategy)
策略模式定義了一系列算法,并將每個(gè)算法封裝起來(lái),使他們可以相互替換,且算法的變化不會(huì)影響到使用算法的客戶。需要設(shè)計(jì)一個(gè)接口,為一系列實(shí)現(xiàn)類提供統(tǒng)一的方法,多個(gè)實(shí)現(xiàn)類實(shí)現(xiàn)該接口,設(shè)計(jì)一個(gè)抽象類(可有可無(wú),屬于輔助類),提供輔助函數(shù)。策略模式的決定權(quán)在用戶,系統(tǒng)本身提供不同算法的實(shí)現(xiàn),新增或者刪除算法,對(duì)各種算法做封裝。因此,策略模式多用在算法決策系統(tǒng)中,外部用戶只需要決定用哪個(gè)算法即可。
public interface ICalculator {
public int calculate(String exp);
}
---------------------------------------------------------
public class Minus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp, "-");
return arrayInt[0] - arrayInt[1];
}
}
---------------------------------------------------------
public class Plus extends AbstractCalculator implements ICalculator {
@Override
public int calculate(String exp) {
int arrayInt[] = split(exp, "\\+");
return arrayInt[0] + arrayInt[1];
}
}
--------------------------------------------------------
public class AbstractCalculator {
public int[] split(String exp, String opt) {
String array[] = exp.split(opt);
int arrayInt[] = new int[2];
arrayInt[0] = Integer.parseInt(array[0]);
arrayInt[1] = Integer.parseInt(array[1]);
return arrayInt;
}
}
public class StrategyTest {
public static void main(String[] args) {
String exp = "2+8";
ICalculator cal = new Plus();
int result = cal.calculate(exp);
System.out.println(result);
}
}
7. 觀察者模式(Observer)
類似于郵件訂閱和 RSS 訂閱,當(dāng)我們?yōu)g覽一些博客或 wiki時(shí),經(jīng)常會(huì)看到 RSS 圖標(biāo),就這的意思是,當(dāng)你訂閱了該文章,如果后續(xù)有更新,會(huì)及時(shí)通知你。其實(shí),簡(jiǎn)單來(lái)講就一句話:當(dāng)一個(gè)對(duì)象變化時(shí),其它依賴該對(duì)象的對(duì)象都會(huì)收到通知,并且隨著變化!對(duì)象之間是一種一對(duì)多的關(guān)系。
public interface Observer {
public void update();
}
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println("observer1 has received!");
}
}
public class Observer2 implements Observer {
@Override
public void update() {
System.out.println("observer2 has received!");
}
}
public interface Subject {
/*增加觀察者*/
public void add(Observer observer);
/*刪除觀察者*/
public void del(Observer observer);
/*通知所有的觀察者*/
public void notifyObservers();
/*自身的操作*/
public void operation();
}
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while (enumo.hasMoreElements()) {
enumo.nextElement().update();
}
}
}
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("update self!");
notifyObservers();
}
}
public class ObserverTest {
public static void main(String[] args) {
Subject sub = new MySubject();
sub.add(new Observer1());
sub.add(new Observer2());
sub.operation();
}
}
8. 代理模式
- 靜態(tài)代理
// 訴訟接口
public interface ILawsiut {
// 提交申請(qǐng)
void submit();
// 進(jìn)行舉證
void burden();
// 開始辯護(hù)
void defend();
// 訴訟完成
void finish();
}
// 訴訟人
public class XiaoMin implements ILawsiut{
@Override
public void submit() {
System.out.println("申請(qǐng)仲裁");
}
@Override
public void burden() {
System.out.println("提交證據(jù)");
}
@Override
public void defend() {
System.out.println("進(jìn)行辯護(hù)");
}
@Override
public void finish() {
System.out.println("訴訟成功");
}
}
// 律師
package proxy;
public class Lawyer implements ILawsiut {
private ILawsiut mLawsuit;
public Lawyer(ILawsiut lawsiut) {
this.mLawsuit = lawsiut;
}
@Override
public void submit() {
mLawsuit.submit();
}
@Override
public void burden() {
mLawsuit.burden();
}
@Override
public void defend() {
mLawsuit.defend();
}
@Override
public void finish() {
mLawsuit.finish();
}
}
public class Client {
public static void main(String[] args){
ILawsiut xiaoMin = new XiaoMin();
ILawsiut lawyer = new Lawyer(xiaoMin);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
- 動(dòng)態(tài)代理
java 提供了一個(gè)動(dòng)態(tài)代理接口InvocationHandler,實(shí)現(xiàn)該接口需要重寫其代理方法invoke。
// 動(dòng)態(tài)代理類
public class DynamicProxy implements InvocationHandler {
private Object obj; // 被代理的類引用
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 調(diào)用被代理類對(duì)象的方法
Object result = method.invoke(obj,args);
return result;
}
}
//////////////////////////
// 修改后的客戶類
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
ILawsiut xiaoMin = new XiaoMin();
// 夠早一個(gè)動(dòng)態(tài)代理
DynamicProxy proxy = new DynamicProxy(xiaoMin);
// 獲取被代理類小民的ClassLoader
ClassLoader loader = xiaoMin.getClass().getClassLoader();
// 動(dòng)態(tài)構(gòu)造一個(gè)代理者律師
ILawsiut lawyer = (ILawsiut) Proxy.newProxyInstance(loader, new Class[]{ILawsiut.class}, proxy);
lawyer.submit();
lawyer.burden();
lawyer.defend();
lawyer.finish();
}
}
靜態(tài)代理與動(dòng)態(tài)代理的區(qū)別在于代理類生成的時(shí)間不同,即根據(jù)程序運(yùn)行前代理類是否已經(jīng)存在,可以將代理分為靜態(tài)代理和動(dòng)態(tài)代理。如果需要對(duì)多個(gè)類進(jìn)行代理,并且代理的功能都是一樣的,用靜態(tài)代理重復(fù)編寫代理類就非常的麻煩,可以用動(dòng)態(tài)代理動(dòng)態(tài)的生成代理類。
設(shè)計(jì)模式雖多,但不要貪杯啊哈哈哈。
