記錄一下個人設計模式理解(不斷更新)
總結一下學習到的設計模式以及為啥需要這樣設計,採取這樣的設計模式有什麼好處(相較於之前的寫法)。
簡單說明總結一下一些原則,面向介面設計,高內聚低耦合,少修改原本的程式碼,易於擴充套件
1,首先說明一下簡單工廠模式、工廠方法模式與抽象工廠模式的區別
比如現在有個需求,生產裝置,耳機,鍵盤。我之前的笨蛋寫法會怎麼寫呢:
1,我就定義耳機類,鍵盤類,然後想要啥就直接new 就完事了,
但是這樣子寫耦合性太高(雖然寫起來挺方便的,但是如果出問題了改起來不方便);
另外建議耳機、鍵盤類可以繼承或者實現同一個類或者介面(外設),便於抽象。
那麼用簡單工廠模式的話怎麼寫:我就直接定義一個factoryclass,裡面有個方法是getDevice(string name),然後在getdevice中
對於傳進來的name進行判斷,然後再返回不同的裝置物件。這樣子是降低了耦合性,但是改起來比如說要新加裝置滑鼠,需要在
getdevice中增加新的判斷。--不符合開放封閉原則
工廠方法模式和簡單工廠有什麼區別,簡單工廠之前就只有一個factory類,如果要新加裝置,就需要修改類裡面方法的判斷條件;
工廠方法則是定義一個工廠介面,介面方法是getProduct,然後定義耳機工廠、鍵盤工廠並且實現裡面的抽象方法來產出物件。
之後呼叫的時候IFactory factory = new MouseFactory,factory.getProduct()來獲取物件,如果要新增加裝置就直接新增
一個相應裝置的工廠
抽象工廠模式的話就更抽象了,需求不再是隻是增加外部裝置這種東西,而是增加記憶體、硬碟這樣的主機裝置從而變成一個電腦,
還是一個factory介面,但是介面中可以定義兩種抽象方法,分別獲取外設、主機的物件。
然後factory的子類A,B重寫兩個方法,就可以IFactory factory = new FactoryA(),factory可以直接獲取到兩個物件
多種不同介面類別的產品了,在一個ifactory介面中獲取
2,策略模式:策略模式封裝相似的演算法,避免過多的if else。策略模式注重的是過程,工廠模式主要是返回一個結果物件。
簡單說明一下過程:定義一個方法的介面,對於這個介面定義的方法可以有多種實現(類似的作用),比如加減乘除(運算)、
然後定義一個Context(環境類),Context的建構函式中傳入策略介面(或者直接set),將介面定義為成員變數,然後conrtext就持有了
這個策略介面的引用,之後的呼叫就可以通過構造時傳入的策略介面來進行方法的呼叫。
--》簡單說明的話就是一個問題有多種解決的辦法,選擇其中的一種即可。
策略模式還可以與工廠模式結合一下。
3,裝飾器模式:允許向一個現有的物件新增新的功能,說來最重要的就是理解相應的UML類圖的結構最重要。
Component介面,定一個method,然後一個ConcreteComponent實現這個介面,這是被裝飾的物件(命名上也是具體物件)
然後一個抽象Decorator實現這個介面,建構函式中傳入Component,重寫的method中呼叫component.method()//暫時看不出來有啥用
然後就是具體的Decorator的實現了,構造就直接super,如果想加點新東西,構造的時候就傳入新的內容,然後重寫
這個method,其中就可以呼叫super.method(),實現物件的裝飾的作用效果。
如果只有一個concretecomponent或者decorator甚至可以直接繼承就可以,不需要實現相同的介面
然後執行的過程:舉個例子
Skills skills = new Skills(hero);
Skills r = new Skill_R(skills,"猛龍擺尾");
Skills e = new Skill_E(r,"天雷破/摧筋斷骨");
Skills w = new Skill_W(e,"金鐘罩/鐵布衫");
Skills q = new Skill_Q(w,"天音波/迴音擊");
//學習技能
q.learnSkills();
public class Skill_Q extends Skills{
private String skillName;
public Skill_Q(Hero hero,String skillName) {
super(hero);
this.skillName = skillName;
}
@Override
public void learnSkills() {
System.out.println("學習了技能Q:" +skillName);
super.learnSkills();
}
正常的執行順序q,然後super,然後super會調到w,為啥呢,因為構造的時候傳入了w的例項,所以就會這樣一直
層層呼叫
}
4,代理模式,簡單說明過程定義一個介面,需要被代理的類實現這個介面,實現這個方法,然後另一個proxy的類構造的時候
傳入介面,然後實現這個介面的方法,實現的方法中可以執行持有介面的方法並且進行擴充套件。
代理也有幾種方式,靜態代理、動態代理和cglib代理
來個簡單demo
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHosue() {
System.out.println("買房前準備");
buyHouse.buyHosue();
System.out.println("買房後裝修");
}
}
這個是靜態代理方面代理類的操作。實際例項化的時候,代理類就可以執行並且擴充套件被代理類的功能了。
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
動態代理,還是需要被代理類impl實現相同的介面,Java提供了proxy類和invocationHandler介面進行
代理生成jdk動態代理類和動態代理物件。
說明一下過程:
1,動態的處理器(執行目標物件的方法時,會觸發處理器的方法),定義一個類實現invocationhandler介面:
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("買房前準備");
Object result = method.invoke(object, args);
System.out.println("買房後裝修");
return result;
}
}
2,說明一下呼叫的過程
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}
cglib的不要求被代理類需要實現介面,但有一個要求被代理類可被繼承,所以被代理類不能是final的。原理暫時說不太清楚
來個demo
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(final Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("買房前準備");
Object result = methodProxy.invoke(object, args);
System.out.println("買房後裝修");
return result;
}
}
public class CglibProxyTest {
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
CglibProxy cglibProxy = new CglibProxy();
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
buyHouseCglibProxy.buyHosue();
}
}
5,來個組合模式的說明,書上的看著有點懵:
又看了一個菜鳥教程的組合模式的說明,感覺就是物件裡面存有同類的其他物件的引用,可以用個列表來進行儲存,
之後的組織結構的顯示就可以從root開始遍歷其他的物件。
emmm,菜鳥模式的例子不好,換個我找的:https://www.cnb·logs.com/chenssy/p/3299719.html
不同的檔案,比如視訊檔案、圖片檔案、資料夾,基本可以有一個基本的抽象方法:內容展示,然後對於資料夾這個子類,
裡面可以持有一個列表,然後定義其他的方法add,remove,遍歷的方法,這樣子還比較合理。
6,原型模式,通過複製自己進行物件的建立,這個java裡面有一個介面是cloneable介面,類實現這個介面並且重寫clone的方法
即可實現淺克隆,即克隆基本型別和string,其他的引用型別的屬性是不能夠克隆的。
具體怎樣體現:原型模式克隆出來的其實是一個新的物件--也就是說會出現在不同的記憶體地址上,但是引用型別的屬性所持有的物件
其實是同一個(記憶體地址相同);
//使用clone()方法實現淺克隆
public WeeklyLog clone()
{
Object obj = null;
try
{
obj = super.clone();
return (WeeklyLog)obj;
}
catch(CloneNotSupportedException e)
{
System.out.println("不支援複製!");
return null;
}
}
Java中如何實現深克隆,實現序列化介面(雖然啥方法都不需要做),類中是需要定義新的clone方法:
//使用序列化技術實現深克隆
public WeeklyLog deepClone() throws IOException, ClassNotFoundException, OptionalDataException
{
//將物件寫入流中
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(this);
//將物件從流中取出
ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (WeeklyLog)ois.readObject();
}
7,模板方法模式:基本就是整合子類的共有方法,抽象到父類中;也可以這樣說明就是抽象父類中定義抽象方法(大致框架),
然後子類繼承父類的時候就需要重寫這些方法,就相當於父類是一套模板,定義了必須要做的事,然後子類具體實現的時候需要
按照這套模板實現基本的要求,之後如果想擴充套件方法就有子類自己決定了。
8,來個建造者模式的demo,這個建造者模式就主要用鏈式的形式說明(也更好看)。
public class House {
//地基
private String base;
//牆
private String wall;
//吊頂
private String cell;
//構造器
public House(Builder builder) {
this.base = builder.base;
this.wall = builder.wall;
this.cell = builder.cell;
}
public static class Builder{
//地基
private String base;
//牆
private String wall;
//吊頂
private String cell;
//set
public Builder base(String base) {
this.base = base;
return this;
}
public Builder wall(String wall) {
this.wall = wall;
return this;
}
public Builder cell(String cell) {
this.cell = cell;
return this;
}
public House build(){
return new House(this);
}
}
//toString方法忽略
}
House house = new House.Builder()
.base("流行房子的地基")
.wall("流行方式的牆")
.cell("流行方式的頂子")
.build();
9,單例模式,主要目標是返回唯一物件例項,然後有多種寫法,什麼懶漢、餓漢、dcl;(私有化構造方法,通過getinstance獲取例項)
懶漢就是一開始需要的時候才建立,但是會有執行緒安全問題;
餓漢就是直接定義類的屬性的時候就直接建立instance的例項,雖然沒有安全問題但是浪費空間。
dcl(double check locking),雙重檢查判斷例項是否為空,外面的一層避免浪費(因為synchonize消耗資源大,
所以先判斷是否需要例項化),之後第二層判斷例項是否為空(雖然加了synchonized關鍵字,只有一個執行緒能夠執行裡面的方法
,但是執行完釋放鎖物件後另一個執行緒也可以執行,不判斷可能會重複建立物件)。
靜態內部類:適用於靜態域情況(這個不是很懂)
列舉:public enum Singleton{
INSTANCE;
public void anyMethod(){
}
},支援自動化序列物件
10,觀察者模式,這個我絕對認真分析,甚至整個demo也可以。
首先是說明一下觀察者模式,自己說明一下結構的話就是類似訂閱釋出,有一個主題subject,主題包含一個屬性列表,列表內
儲存著訂閱者(即Observer,觀察者),然後主題含有add、removeObserver的方法來新增刪除觀察者以及一個notification的
通知方法通知所有觀察者Observer,通知方法中呼叫了Observer的update方法,即subject改變了狀態則呼叫通知方法通知所有
觀察者,然後就會執行觀察者註冊的update方法,從而實現變化。
這種更好說明的話是一種設計的思想,可以達到這樣的效果,結構倒是可以變化,比如變化成監聽機制:來個demo說明一下。
Java自定義監聽事件,主要涉及到有三個部分,eventsource、event、eventListener,說明一下:
eventSource,事件源,比如說是一個button;
event,事件,對於一個事件源可能有多種事件與之相關,button可能是點選、可能是長按等事件
eventListener,事件監聽者,監聽到狀態變化就做自己註冊的事情。
自己定義一個監聽demo:這個event不太懂作用
public class Kid {
private List<ParentListener> mListeners = new ArrayList<>();
public Kid() {
}
public void addListener(ParentListener parentListener) {
mListeners.add(parentListener);
}
public void removeListener(ParentListener parentListener) {
mListeners.remove(parentListener);
}
public void notifyListener(PlayingEvent playingEvent) {
for (ParentListener listener : mListeners) {
listener.detectKidIsPlaying(playingEvent);
}
}
public void PlayingGame(){
System.out.println("玩遊戲真不錯");
notifyListener(new PlayingEvent(this,"lol"));
}
public void WatchTv(){
System.out.println("看電視真不錯");
}
public static void main(String[] args) {
Kid kid = new Kid();
kid.addListener(new FatherListener());
kid.addListener(new MotherListener());
kid.WatchTv();
kid.PlayingGame();
}
}
public interface ParentListener {
void detectKidIsPlaying(PlayingEvent playingEvent);
}
11,外觀模式,簡單來說的話就是封裝內部的具體實現,暴露給外面的只有簡單的方法呼叫。
外觀模式(Facade Pattern)隱藏系統的複雜性,並向客戶端提供了一個客戶端可以訪問系統的介面。
這種型別的設計模式屬於結構型模式,它向現有的系統新增一個介面,來隱藏系統的複雜性。
這是菜鳥教程上的說明。
12,狀態模式,這個的應用場景主要是物件的行為與物件的狀態有關,
比如說需要在方法裡對於物件的狀態做判斷然後去做不同的事情,如果狀態很複雜的話就需要很多的ifelse,
看起來複雜也不好修改,如果要新加條件的話還需要大量改原來的程式碼,不建議
然後這種模式下的優點就是將狀態抽象出來,便於修改
來個demo:
比如說工作,工作有不同的工作狀態,根據不同的工作狀態來決定幹活的效率;
public class Work {//實際呼叫working的時候就是呼叫working的成員變數state的方法,然後根據不同的state來做不同的行為
private State current;
private int hour;
public Work() {
this.current = new ForenoonState();
}
public State getCurrent() {
return current;
}
public void setCurrent(State current) {
this.current = current;
}
public int getHour() {
return hour;
}
public void setHour(int hour) {
this.hour = hour;
}
public void woking(){
current.working(this);
}
}
public class ForenoonState extends State{
@Override
public void working(Work work) {
if(work.getHour()<10){
System.out.println("我好精神");
}else {
work.setCurrent(new NoonState());
work.woking();
}
}
13,介面卡模式,將一個類的介面轉換成客戶希望的另外一個介面。介面卡模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。
這裡我覺得菜鳥教程的demo挺好的,指的是當前的audioplayer想要擴充功能,不止是播放audio,還能播放MP4
adapter需要實現與audioplayer相同的介面方法,並且audioplayer內持有該adapter引用,然後要擴充套件的時候可以通過這個adpter來調別的方法
從而達到擴充套件的效果,為什麼不直接調新的介面 為了解耦。
14,備忘錄模式,這種設計模式主要目標就是儲存目標某個時候的狀態,主要涉及到三個物件;
originator、meme、caretaker,例子的話菜鳥教程的比較明確了:
public class Memento {//儲存狀態
private String state;
public Memento(String state){
this.state = state;
}
public String getState(){
return state;
}
}
public class Originator {//發起人
private String state;
public void setState(String state){
this.state = state;
}
public String getState(){
return state;
}
public Memento saveStateToMemento(){
return new Memento(state);
}
public void getStateFromMemento(Memento Memento){
state = Memento.getState();
}
}
public class CareTaker {//儲存多個狀態
private List<Memento> mementoList = new ArrayList<Memento>();
public void add(Memento state){
mementoList.add(state);
}
public Memento get(int index){
return mementoList.get(index);
}
}
15,迭代器模式的話,就是itertor,
16,橋接模式,將抽象與實現分開從而實現各自變化,這個說不太清楚,現在簡單的理解的話
就是一個是具體的類,另一個則是這個具體的類要完成的動作,然後可以將他的動作抽象成一個介面,
然後被這個具體的類持有引用,這樣擴充套件起來很方便,甚至這個具體的類都可以是個抽象類,
這就是我暫時理解的抽象與實現分離實現變化。
17,命令模式,將請求封裝為物件,然後對於請求可以進行排隊記錄,還可以撤銷;
大致的結構:invoker(呼叫處理請求),invoker內持有一個command列表,然後invoker可以對於命令增刪
command持有receiver引用,直接就是receiver執行。
18,責任鏈模式,使多個物件都有機會處理請求,連成鏈不斷傳遞請求,直到有物件能夠處理;
這種主要是一種結構的寫法:
比如一個handler類,類裡面定義一個Handler next,然後setnext(next1),然後處理的請求中
如果自己處理不了這個請求,就判斷自己的next是否為空,如果不為空,則呼叫next的相同方法來處理請求。
19,中介者模式(Mediator),用一箇中介物件來封裝一系列物件的互動。怎麼說,有點像聯合國加好多國家這樣的結構;
所以基本就是這個中介者持有好多類的引用,然後互相的呼叫是通過這個中介者的,雖然降低了類之間的耦合
但是中介者會很複雜。
20,享元模式,共享物件:運用共享技術有效地支援大量細粒度的物件;(flyweight):
減少物件的建立 比如池的技術;
http://c.biancheng.net/view/1371.html 這個清楚,內部狀態,外部狀態,內部狀態共享,享元角色,外部狀態可改變,
非享元角色。然後享元工廠的角色就是決定是否需要建立一個享元角色,如果已經存在,返回的就是已經存在的,如果
不存在則是建立一個新的享元物件。
21,直譯器模式,這個沒太看懂
22,訪問者模式,物件結構比較穩定,但經常需要在此物件結構上定義新的操作。
需要對一個物件結構中的物件進行很多不同的並且不相關的操作,而需要避免這些操作“汙染”這些物件的類,
也不希望在增加新操作時修改這些類。
來個demo:
public abstract class Vistor{
public abstract void VisitConcreteElementA(ConcreterElementA concreteelementa);
public abstract void VisitConcreteElementB(ConcreterElementB concreteelementb);
}
然後定義具體的訪問者來實現這兩個方法;
public abstract class Element{
public abstract void accept(Vistor vistor);
}
public class ConcreterElementA extends Element{
public void accept(Vistor vistor){
vistor.VisitConcreteElementA(this);
}
}
這種結構期待的是主體穩定,然後可以新增vistor對於主題進行新的操作;