泛化 = 實現 > 組合 > 聚合 > 關聯 > 依賴javascript
一我的(Person)能夠買車(car)和房子(House),那麼就能夠稱:Person類依賴於Car類和House類
這裏注意與下面的關聯關係區分:Person類裏並無使用Car和House類型的屬性,Car和House的實例是以參量的方式傳入到buy()方法中。
依賴關係在Java語言中體現爲局域變量、方法的形參,或者對靜態方法的調用。php
它使一個類知道另外一個類的屬性和方法。
關聯能夠是雙向的,也能夠是單向的。
在Java語言中,關聯關係通常使用成員變量來實現。java
聚合是關聯關係的一種,是強的關聯關係。
聚合是總體和個體之間的關係,但個體能夠脫離總體而存在。
例如,汽車類與引擎類、輪胎類,以及其它的零件類之間的關係便總體和個體的關係。
與關聯關係同樣,聚合關係也是經過成員變量實現的。可是關聯關係所涉及的兩個類是處在同一層次上的,而在聚合關係中,兩個類是處在不平等層次上的,一個表明總體,另外一個表明部分。算法
組合是關聯關係的一種,是比聚合關係強的關係,也以成員變量的形式出現。
在某一個時刻,部分對象只能和一個總體對象發生組合關係,由後者排他地負責生命週期。
部分和總體的生命週期同樣。
總體能夠將部分傳遞給另外一個對象,這時候該部分的生命週期由新總體控制,而後舊總體能夠死亡。編程
一個類中的一些行爲,可能會隨着系統的迭代而發生變化。爲了使得該類知足開放-封閉原則(即:具有可擴展性 或 彈性),咱們須要將這些將來會發生動態變化的行爲從該類中剝離出來,並經過預測將來業務發展的方式,爲這些行爲抽象出共有的特徵,封裝在抽象類或接口中,並經過它們的實現類提供具體的行爲。本來類中須要持有該抽象類/接口的引用。在使用時,將某一個具體的實現類對象注入給該類所持有的接口/抽象類的引用。設計模式
若是類A中有兩個行爲X和Y會隨着業務的發展而變化,那麼,咱們須要將這兩個行爲從類A中剝離出來,並造成各自的繼承體系(策略體系)。每一個繼承體系(策略體系)的頂層父類/接口中定義共有行爲的抽象函數,每一個子類/實現類中定義該策略體系具體的實現。安全
其中,每個被抽象出來的繼承體系被稱爲一個策略體系,每一個具體的實現類被稱爲策略。微信
此時,策略體系已經構建完成,接下來須要改造類A。
在類A中增長所需策略體系的頂層父類/接口,並向外暴露一個共有的函數action給調用者使用。多線程
在Spring項目中,策略類和類A之間的依賴關係能夠經過依賴注入來完成。架構
到此爲止,策略模式已經構建完成,下面咱們來看優缺點分析。
若是類A須要更換一種策略的時候,只需修改Spring的XML配置文件便可,其他全部的代碼均不須要修改。
好比,將類A的策略X_1更換成X_2的方法以下:
<bean id="a" class="類A">
<!-- 將本來策略實現類X_1修改成策略實現類X_2便可 -->
<property name="策略接口X" class="策略實現類X_2" />
</bean>
此外,若是須要新增一種策略,只須要爲策略接口X添加一個新的實現類便可,並覆蓋其中的commonAction函數。而後按照上面的方式修改XML文件便可。
在這個過程當中,在保持原有Java代碼不發生變化的前提下,擴展性了新的功能,從而知足開放封閉原則。
若是咱們須要根據不一樣的策略建立多種類A的對象,那麼使用策略模式就能很容易地實現這一點。
好比,咱們要建立三個A類的對象,a、b、c。其中,a使用策略X_1和Y_1,b使用策略X_2和Y_2,c使用策略X_3和Y_3。
要建立這三個對象,咱們只需在XML中做以下配置便可:
<bean id="a" class="類A">
<property name="策略接口X" class="策略實現類X_1" />
<property name="策略接口Y" class="策略實現類Y_1" />
</bean>
<bean id="b" class="類A">
<property name="策略接口X" class="策略實現類X_2" />
<property name="策略接口Y" class="策略實現類Y_2" />
</bean>
<bean id="c" class="類A">
<property name="策略接口X" class="策略實現類X_3" />
<property name="策略接口Y" class="策略實現類Y_3" />
</bean>
問:如何實現部分繼承?也就是類Son1只繼承Father的一部分功能,Son2繼承Father的另外一部分功能。
這是設計上的缺陷,當出現這種狀況時,應當將父類再次拆分紅2個子類,保證任何一個父類的行爲和特徵均是該繼承體系中共有的!
問:隨着需求的變化,父類中須要增長共有行爲時怎麼辦?這就破壞了「開放封閉原則」。
這並未破壞「開放封閉原則」!在系統迭代更新的過程當中,修改原有的代碼是在所不免的,這並不違背「開放封閉原則」。
「開放封閉原則」要求咱們:當系統在迭代過程當中,第一次出現某一類型的需求時,是容許修改的;在此時,應該對系統進行修改,並進行合理地設計,以保證對該類型需求的再次修改具有可擴展性。當再一次出現該類型的需求時,就不該該修改原有代碼,只容許經過擴展來知足需求。
若是出現以下場景需求時,就須要使用觀察者模式。
若是存在一系列類,他們都須要向指定類獲取指定的數據,當獲取到數據後須要觸發相應的業務邏輯。這種場景就能夠用觀察者模式來實現。
在觀察者模式中,存在兩種角色,分別是:觀察者和被觀察者。被觀察者即爲數據提供者。他們呈多對一的關係。
被觀察者是數據提供方,觀察者是數據獲取方
一個普通的類,若是要成爲觀察者,獲取指定的數據,一共須要以下幾步:
首先,須要實現Observer接口,並實現update函數;
而後,在該函數中定義獲取數據後的業務邏輯;
update(Observable, Object)一共有兩個參數:
Observable:被觀察者對象(數據提供方)
Object:數據自己
最後,經過調用 被觀察者 的addObservable()或者經過Spring的XML配置文件完成觀察者向被觀察者的注入。此時,該觀察者對象就會被添加進 被觀察者 的List中。
調用者纔是真正的數據提供方。當調用者須要廣播最新數據時,只需調用 被觀察者 的notidyObservers()函數,該函數會遍歷List集合,並依次調用每一個Observer的update函數,從而完成數據的發送,並觸發每一個Observer收到數據後的業務邏輯。
將Observer註冊進Observable中有以下兩種方式:
在系統運行前,若是觀察者數量能夠肯定,並在運行過程當中不會發生變化,那麼就能夠在XML中完成List對象的注入,這種方式代碼將會比較簡潔。
一、配置好全部 觀察者 bean
<!-- 建立observerA -->
<bean name="observerA" class="ObservserA">
</bean>
<!-- 建立observerB-->
<bean name="observerB" class="ObservserB">
</bean>
二、 配置好 被觀察者 bean,並將全部觀察者bean注入給被觀察者bean
<!-- 建立observable -->
<bean name="observable" class="Observable">
<property name="observerList">
<list>
<ref bean="observerA" />
<ref bean="observerB" />
</list>
</property>
</bean>
在Spring初始化的時候,經過addObserver()函數將全部Observer對象注入Observable的observerList中。
@Component
public class InitConfiguration implements ApplicationListener<ContextRefreshedEvent>{
@Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
if(event.getApplicationContext().getParent() == null){
Observable observable = (Observable)event.getApplicationContext().getBean("observable");
ObserverA observerA = (ObserverA)event.getApplicationContext().getBean("observerA");
ObserverB observerB = (ObserverB)event.getApplicationContext().getBean("observerB");
observable.setObserverList(Arrays.asList(observerA, observerB));
}
}
}
建議使用第一種方式初始化全部的觀察者,此外,被觀察者仍然須要提供addObserver()函數供系統在運行期間動態地添加、刪除觀察者對象。
JDK已經提供了觀察者模式的工具包,包括Observable類和Observer接口。若要實現觀察者模式,直接使用這兩個工具包便可。
須要加強一個對象中某些函數的功能。
須要動態地給一個對象增長功能,這些功能能夠再動態地撤銷。
須要增長 由一些基本功能排列組合 而產生的大量功能,從而使繼承體系大爆炸。
抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。
具體裝飾(Concrete Decorator)角色:負責給構件對象」貼上」附加的責任。
Decorator中包含Component的成員變量,每一個Concrete Decorator實現類均須要實現operation()函數,該函數大體過程以下:
class ConcreteDecorator {
private Component component;
返回類型 operation(){
// 執行上一層的operation(),並獲取返回結果
返回結果 = component.operation();
// 拿到返回結果後,再作額外的處理
處理返回結果
return 返回結果;
}
}
使用裝飾類的過程以下:
// 準備好全部裝飾類
DecoratorA decoratorA = new DecoratorA();
DecoratorB decoratorB = new DecoratorB();
DecoratorC decoratorC = new DecoratorC();
// 準備好 被裝飾的類
Component component = new Component();
// 組裝裝飾類
decoratorC.setComponent(decoratorB);
decoratorB.setComponent(decoratorA);
decoratorA.setComponent(component);
// 執行
decoratorC.operation();
執行過程以下:
一、Decorator模式與繼承關係的目的都是要擴展對象的功能,可是Decorator能夠提供比繼承更多的靈活性。繼承經過覆蓋的方式重寫須要擴展的函數,固然也能夠經過super.xxx()獲取本來的功能,而後在該功能基礎上擴展新功能,但它只能增長某一項功能;若是要經過繼承實現增長多種功能,那麼須要多層繼承多個類來實現;而Decorator模式能夠在原有功能的基礎上經過組合來增長新功能,這些新功能已經被封裝成一個個獨立的裝飾類,在運行期間經過搭積木的方式選擇裝飾類拼湊便可。
二、經過使用不一樣的具體裝飾類以及這些裝飾類的排列組合,設計師能夠創造出不少不一樣行爲的組合。
一、這種比繼承更加靈活機動的特性,也同時意味着更加多的複雜性。
二、裝飾模式會致使設計中出現許多小類,若是過分使用,會使程序變得很複雜。
三、裝飾模式是針對抽象組件(Component)類型編程。可是,若是你要針對具體組件編程時,就應該從新思考你的應用架構,以及裝飾者是否合適。固然也能夠改變Component接口,增長新的公開的行爲,實現「半透明」的裝飾者模式。在實際項目中要作出最佳選擇。
多用組合,少用繼承。
利用繼承設計子類的行爲,是在編譯時靜態決定的,並且全部的子類都會繼承到相同的行爲。然而,若是可以利用組合的作法擴展對象的行爲,就能夠在運行時動態地進行擴展。
Java中單例(Singleton)模式是一種普遍使用的設計模式。單例模式的主要做用是保證在Java程序中,某個類只有一個實例存在。一些管理器和控制器常被設計成單例模式。
單例模式有不少好處,它可以避免實例對象的重複建立,不只能夠減小每次建立對象的時間開銷,還能夠節約內存空間;可以避免因爲操做多個實例致使的邏輯錯誤。若是一個對象有可能貫穿整個應用程序,並且起到了全局統一管理控制的做用,那麼單例模式也許是一個值得考慮的選擇。
單例模式有不少種寫法,大部分寫法都或多或少有一些不足。下面將分別對這幾種寫法進行介紹。
public class Singleton{
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return instance;
}
}
類的構造函數定義爲private,保證其餘類不能實例化此類;
而後提供了一個靜態實例並返回給調用者;
餓漢模式在類加載的時候就對實例進行建立,實例在整個程序週期都存在
優勢:只在類加載的時候建立一次實例,不會存在多個線程建立多個實例的狀況,避免了多線程同步的問題。
缺點:即便這個單例沒有用到也會被建立,並且在類加載以後就被建立,內存就被浪費了。
適用場景:這種實現方式適合單例佔用內存比較小,在初始化時就會被用到的狀況。可是,若是單例佔用的內存比較大,或單例只是在某個特定場景下才會用到,使用餓漢模式就不合適了,這時候就須要用到懶漢模式進行延遲加載。
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
懶漢模式中單例是在須要的時候纔去建立的,若是單例已經建立,再次調用獲取接口將不會從新建立新的對象,而是直接返回以前建立的對象。
若是某個單例使用的次數少,而且建立單例消耗的資源較多,那麼就須要實現單例的按需建立,這個時候使用懶漢模式就是一個不錯的選擇。
可是這裏的懶漢模式並無考慮線程安全問題,在多個線程可能會併發調用它的getInstance()方法,致使建立多個實例,所以須要加鎖解決線程同步問題,實現以下。
public class Singleton{
private static Singleton instance = null;
private Singleton(){}
public static synchronized Singleton newInstance(){
if(null == instance){
instance = new Singleton();
}
return instance;
}
}
加鎖的懶漢模式看起來即解決了線程併發問題,又實現了延遲加載,然而它存在着性能問題,依然不夠完美。synchronized修飾的同步方法比通常方法要慢不少,若是屢次調用getInstance(),累積的性能損耗就比較大了。
public class Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
這種方式比上一種方式只多加了一行代碼,那就是在synchronized之上又加了一層判斷if (instance == null)。這樣當單例建立完畢後,不用每次都進入同步代碼塊,從而能提高效率。固然,除了初始化單例對象的線程ThreadA外,可能還存在少數線程,在ThreadA建立完單例後,剛釋放鎖的時候進入同步代碼塊,但此時有第二道if (instance == null)判斷,所以也就避免了建立多個對象。並且進入同步代碼塊的線程相對較少。
public class Singleton{
private static class SingletonHolder{
public static Singleton instance = new Singleton();
}
private Singleton(){}
public static Singleton newInstance(){
return SingletonHolder.instance;
}
}
這種方式一樣利用了類加載機制來保證只建立一個instance實例。它與餓漢模式同樣,也是利用了類加載機制,所以不存在多線程併發的問題。不同的是,它是在內部類裏面去建立對象實例。這樣的話,只要應用中不使用內部類,JVM就不會去加載這個單例類,也就不會建立單例對象,從而實現懶漢式的延遲加載。也就是說這種方式能夠同時保證延遲加載和線程安全。
public enum Singleton{
instance;
public void whateverMethod(){}
}
上面提到的四種實現單例的方式都有共同的缺點:
一、須要額外的工做來實現序列化,不然每次反序列化一個序列化的對象時都會建立一個新的實例。
二、可使用反射強行調用私有構造器(若是要避免這種狀況,能夠修改構造器,讓它在建立第二個實例的時候拋異常)。
而枚舉類很好的解決了這兩個問題,使用枚舉除了線程安全和防止反射調用構造器以外,還提供了自動序列化機制,防止反序列化的時候建立新的對象。所以,《Effective Java》做者推薦使用的方法。不過,在實際工做中,不多看見有人這麼寫。
在父類中定義算法的流程,而算法的某些沒法肯定的細節,經過抽象函數的形式,在子類中去實現。
也能夠理解爲,一套算法的某些步驟可能隨着業務的發展而改變,那麼咱們能夠將肯定的步驟在父類中實現,而可變的步驟做爲抽象函數讓其在子類中實現。
在模板方法模式中,父類是一個抽象類,算法的每一步都被封裝成一個函數,templateMethod函數將全部算法步驟串聯起來。
對於不變的步驟,用private修飾,防止子類重寫;
對於可變的步驟,用abstract protected修飾,必需要求子類重寫;
子類重寫完全部抽象函數後,調用templateMethod便可執行算法。
外觀模式這種思想在項目中廣泛存在,也極其容易理解,你們必定用過,只是沒有上升到理論的層面。這裏對這種思想進行介紹。
外觀模式他屏蔽了系統功能實現的複雜性,向客戶端提供一套極其簡單的接口。客戶端只須要知道接口提供什麼功能,如何調用就好了,不須要管這些接口背後是如何實現的。從而使得客戶端和系統之間的耦合度大大下降,客戶端只需跟一套簡單的Facade接口打交道便可。
做爲一個基金交易平臺,須要提供一套接口規範,供各個基金公司接入。然而,各個基金公司的接口各不相同,沒有辦法直接和平臺接口對接。此時,各個基金公司須要自行實現一個適配器,適配器完成不一樣接口的轉換工做,使得基金公司的接口和平臺提供的接口對接上。
適配器模式有三種實現方式,下面都以基金交易平臺的例子來解釋。
基金公司的交易接口:
/**
* 基金公司的交易接口
*/
class FundCompanyTrade{
/**
* 買入函數
*/
public void mairu() {
// ……
}
/**
* 賣出函數
*/
public void maichu() {
// ……
}
}
基金交易平臺的交易接口
/**
* 基金交易平臺的交易接口
*/
interface FundPlatformTrade {
// 買入接口
void buy();
// 賣出接口
void sell();
}
基金交易平臺均經過以下代碼調用各個基金公司的交易接口:
class Client {
@Autowired
private FundPlatformTrade fundPlatformTrade;
/**
* 買入基金
*/
public void buy() {
fundPlatformTrade.buy();
}
/**
* 賣出基金
*/
public void sell() {
fundPlatformTrade.sell();
}
}
經過繼承來實現接口的轉換。
基金交易適配器:
class Adapter extends FundCompanyTrade implements FundPlatformTrade {
void buy() {
mairu();
}
void sell(){
maichu();
}
}
適配器Adapter繼承了FundCompanyTrade,所以擁有了FundCompanyTrade買入和賣出的能力;適配器Adapter又實現了FundPlatformTrade,所以須要實現其中的買入和賣出接口,這個過程便完成了基金公司交易接口向基金平臺交易接口的轉換。
使用時,只需將Adapter經過Spring注入給Client類的fundPlatformTrade成員變量便可:
<!-- 聲明Adapter對象 -->
<bean name="adapter" class="Adapter">
</bean>
<!-- 將adapter注入給Client -->
<bean class="Client">
<property name="fundPlatformTrade" ref="adapter" />
</bean>
經過組合來實現接口的轉換。
基金交易適配器:
class Adapter implements FundPlatformTrade {
@Autowired
private FundCompanyTrade fundCompanyTrade;
void buy() {
fundCompanyTrade.mairu();
}
void sell(){
fundCompanyTrade.maichu();
}
}
這種方式中,適配器Adapter並未繼承FundCompanyTrade,而是將該對象做爲成員變量注入進來,同樣能夠達到一樣的效果。
當存在這樣一個接口,其中定義了N多的方法,而咱們如今卻只想使用其中的一個到幾個方法,若是咱們直接實現接口,那麼咱們要對全部的方法進行實現,哪怕咱們僅僅是對不須要的方法進行置空(只寫一對大括號,不作具體方法實現)也會致使這個類變得臃腫,調用也不方便,這時咱們可使用一個抽象類做爲中間件,即適配器,用這個抽象類實現接口,而在抽象類中全部的方法都進行置空,那麼咱們在建立抽象類的繼承類,並且重寫咱們須要使用的那幾個方法便可。
目標接口:A
public interface A {
void a();
void b();
void c();
void d();
void e();
void f();
}
適配器:Adapter
實現全部函數,將全部函數先置空。
public abstract class Adapter implements A {
public void a(){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void b(){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void c(){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void d(){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void e(){
throw new UnsupportedOperationException("對象不支持這個功能");
}
public void f(){
throw new UnsupportedOperationException("對象不支持這個功能");
}
}
實現類:Ashili
繼承適配器類,選擇性地重寫相應函數。
public class Ashili extends Adapter {
public void a(){
System.out.println("實現A方法被調用");
}
public void d(){
System.out.println("實現d方法被調用");
}
}
迭代器模式用於在無需瞭解容器內部細節的狀況下,實現容器的迭代。
容器用於存儲數據,而容器的存儲結構種類繁多。在不使用適配器模式的狀況下,若是要迭代容器中的元素,就須要充分理解容器的存儲結構。存儲結構不一樣,致使了不一樣容器的迭代方式都不同。這無疑增長了咱們使用容器的成本。
而迭代器模式提出了一種迭代容器元素的新思路,迭代器規定了一組迭代容器的接口,做爲容器使用者,只需會用這套迭代器便可。容器自己須要實現這套迭代器接口,並實現其中的迭代函數。也就是容器提供方在提供容器的同時,還須要提供迭代器的實現。由於容器自己是瞭解本身的存儲結構的,由它來實現迭代函數很是合適。而咱們做爲容器的使用者,只需知道怎麼用迭代器便可,無需瞭解容器內部的存儲結構。
迭代器 Iterator:封裝了迭代容器的接口
容器 Container:存儲元素的東西
容器若要具有迭代的能力,就必須擁有getIterator()函數,該函數將會返回一個迭代器對象
每一個容器都有屬於本身的迭代器內部類,該內部類實現了Iterator接口,並實現了其中用於迭代的兩個函數hasNext()和next()
boolean hasNext():用於判斷當前容器是否還有還沒有迭代完的元素
Object next():用於獲取下一個元素
迭代器接口:
public interface Iterator {
public boolean hasNext();
public Object next();
}
容器接口:
public interface Iterator {
public boolean hasNext();
public Object next();
}
具體的容器(必須實現Container接口):
public class NameRepository implements Container {
public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
具體的容器實現了Container接口,並實現了其中的getIterator()函數,該函數用於返回該容器的迭代器對象。
容器內部須要實現本身的迭代器內部類,該內部類實現Iterator接口,並實現了其中的hasNext()和next()函數。
當容器和容器的迭代器建立完畢後,接下來就輪到用戶使用了,使用就很是簡單了:
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
對於使用者而言,只要知道Iterator接口,就可以迭代全部不一樣種類的容器了。
組合模式定義了樹形結構的物理存儲方式。
現實世界中樹形結構的東西,在代碼實現中,均可以用組合模式來表示。
好比:多級菜單、公司的組織結構等等。
下面就以多級菜單爲例,介紹組合模式。
假設咱們要實現一個多級菜單,並實現多級菜單的增刪改查操做。菜單以下:
一級菜單A
二級菜單A_1
三級菜單A_1_1
三級菜單A_1_2
三級菜單A_1_3
二級菜單A_2
一級菜單B
二級菜單B_1
二級菜單B_2
二級菜單B_3
二級菜單B_4
三級菜單B_4_1
三級菜單B_4_2
三級菜單B_4_3
一級菜單C
二級菜單C_1
二級菜單C_2
二級菜單C_3
菜單的特色以下:
深度不限,能夠有無限級菜單
每層菜單數量不限
Item表示樹中的節點;
Item中包含兩個成員變量:
parent:指向當前節點的父節點
childList:當前節點的子節點列表
這種Item中又包含Item的關係就構成了組合模式。
在構建樹的過程當中,可能會出現循環引用,從而在遍歷樹的時候可能就會出現死循環。所以,咱們須要在添加節點的時候避免循環引用的出現。
咱們能夠在Item中再添加一個List成員變量,用於記錄根節點到當前節點的路徑。該路徑能夠用每一個節點的ID表示。一旦新加入的節點ID已經出如今當前路徑中的時候,就說明出現了循環引用,此時應該給出提示。
若是一個函數中出現大量的、複雜的if-else判斷,這時候就要考慮使用狀態模式了。
由於大量的if-else中每每包含了大量的業務邏輯,頗有可能會隨着業務的發展而變化。若是將這些業務邏輯都寫死在一個類中,那麼當業務邏輯發生變化的時候就須要修改這個類,從而違反了開放封閉原則。而狀態模式就能很好地解決這一問題。
狀態模式將每個判斷分支都封裝成一個獨立的類,每個判斷分支成爲一種「狀態」,所以每個獨立的類就成爲一個「狀態類」。而且由一個全局狀態管理者Context來維護當前的狀態。
在狀態模式中,每個判斷分支被成爲一種狀態,每一種狀態,都會被封裝成一個單獨的狀態類;
全部的狀態類都有一個共同的接口——State
State接口中有一個doAction函數,每一個狀態類的狀態處理邏輯均在該函數中完成;必須將Context對象做爲doAction函數的參數傳入。該函數的結構以下:
class StateA implements State{
public void doAction(Context context){
if (知足條件) {
// 執行相應的業務邏輯
}
else {
// 設置下一跳狀態
context.setState(new StateB());
// 執行下一跳狀態
context.doCurState();
}
}
}
每一個狀態類的doAction函數中都有且僅有一對if-else,if中填寫知足條件時的業務邏輯,而else中填寫不知足條件時的業務邏輯。
else中的代碼都同樣,有且僅有這兩步:
首先將context的state設爲下一個狀態對象;
而後調用context的doCurState()執行;
Context類其實就是本來包含那個巨大、複雜的if-else的類。該類中持有了State對象,表示當前要執行的狀態對象。
Context類必需要有一個doCurState函數,該函數的代碼都同樣:state.doAction()
開啓狀態判斷過程的代碼以下:
// 準備好第一個狀態
StateA stateA = new StateA();
// 設置第一個狀態
context.setState(stateA);
// 開始執行
context.doCurState();
狀態模式將本來在一個類中的龐大的if-else拆分紅一個個獨立的狀態類。本來這個包含龐大if-else的類成爲Context,包含了當前的狀態。Context只須要知道起始狀態類便可,不須要知道其餘狀態類的存在。也就是Context只與第一個狀態類發生耦合。而每個狀態類只和下一個狀態類發生耦合,從而造成一條狀態判斷鏈。狀態類之間的耦合經過Spring XML文件配置。這樣,當判斷邏輯發生變化的時候,只須要新增狀態類,並經過修改XML的方式將新的狀態類插入到判斷邏輯中。從而知足了開放封閉原則。
代理模式
代理模式是在不改變目標類和使用者的前提下,擴展該類的功能。
代理模式中存在『目標對象』和『代理對象』,它們必須實現相同的接口。用戶直接使用代理對象,而代理對象會將用戶的請求交給目標對象處理。代理對象能夠對用戶的請求增長額外的處理。
首先你得擁有一個目標對象,該對象必需要實現一個接口:
public interface Subject
{
public void doSomething();
}
public class RealSubject implements Subject
{
public void doSomething()
{
System.out.println( "call doSomething()" );
}
}
其次,爲目標對象增長額外的邏輯:
自定義一個類,並實現InvocationHandler接口;
實現invoke函數,並將須要增長的邏輯寫在該函數中;
public class ProxyHandler implements InvocationHandler
{
private Object proxied;
public ProxyHandler( Object proxied )
{
this.proxied = proxied;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable
{
//在轉調具體目標對象以前,能夠執行一些功能處理
//轉調具體目標對象的方法
return method.invoke( proxied, args);
//在轉調具體目標對象以後,能夠執行一些功能處理
}
}
建立代理對象,調用者直接使用該對象便可:
RealSubject real = new RealSubject();
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class},
new ProxyHandler(real));
proxySubject.doSomething();
轉自:http://blog.csdn.net/u010425776/article/details/79211117
文章有不當之處,歡迎指正,你也能夠關注個人微信公衆號:好好學java
,獲取java等優質視頻教程、學習資源。