常用的设计模式总结
(一)單例模式
簡單點說,就是一個應用程序中,某個類的實例對象只有一個,你沒有辦法去new,因為構造器是被private修飾的,一般通過getInstance()的方法來獲取它們的實例。getInstance()的返回值是一個對象的引用,并不是一個新的實例,所以不要錯誤的理解成多個對象。單例模式實現起來也很容易,直接看demo吧
?
?
(二)觀察者模式?
對象間一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都得到通知并被自動更新。Android中的各種Listener就使用到了這一設計模式,只要用戶對手機進行操作,對應的listener就會被通知,并作出響應的處理
?
?
看不懂圖的人端著小板凳到這里來,給你舉個例子:假設有三個人,小美(女,28),老王和老李。小美很漂亮,很風騷,老王和老李是兩個中年男屌絲,時刻關注著小美的一舉一動。有一天,小美說了一句:我老公今天不在家,一個人好無聊啊~~~,這句話被老王和老李聽到了,結果樂壞了,蹭蹭蹭,沒一會兒,老王就沖到小美家門口了,于是進門了……………………..帕~啪啪啪啪啪~
在這里,小美是被觀察者,老王和老李是觀察者,被觀察者發出一條信息,然后被觀察者進行相應的處理,看代碼:
?
這個接口相當于老王和老李的電話號碼,小美發送通知的時候就會撥打getMessage這個電話,撥打電話就是調用接口,看不懂沒關系,先往下看
public class LaoWang implements Person {private String name = "老王";public LaoWang() {}@Overridepublic void getMessage(String s) {System.out.println(name + "接到了小美打過來的電話,電話內容是:" + s);}}public class LaoLi implements Person {private String name = "老李";public LaoLi() {}@Overridepublic void getMessage(String s) {System.out.println(name + "接到了小美打過來的電話,電話內容是:->" + s);}}?
? 代碼很簡單,我們再看看小美的代碼:
public class XiaoMei {List<Person> list = new ArrayList<Person>();public XiaoMei(){}public void addPerson(Person person){list.add(person);}//遍歷list,把自己的通知發送給所有暗戀自己的人public void notifyPerson() {for(Person person:list){person.getMessage("今天家里就我一個人,你們過來吧,誰先過來誰就能得到我!");}} }我們寫一個測試類來看一下結果對不對
public class Test {public static void main(String[] args) {XiaoMei xiao_mei = new XiaoMei();LaoWang lao_wang = new LaoWang();LaoLi lao_li = new LaoLi();//老王和老李在小美那里都注冊了一下 xiao_mei.addPerson(lao_wang);xiao_mei.addPerson(lao_li);//小美向老王和老李發送通知 xiao_mei.notifyPerson();} }
運行結果我截圖了?
?
我覺得很形象了,通俗易懂,哈哈哈
?
?
(三)裝飾者模式?
對已有的業務邏輯進一步的封裝,使其增加額外的功能,如java中的IO流就使用了裝飾者模式,用戶在使用的時候,可以任意組裝,達到自己想要的效果。?
舉個栗子,我想吃三明治,首先我需要一根大大的香腸,我喜歡吃奶油,在香腸上面加一點奶油,再放一點蔬菜,最后再用兩片面包加一下,很豐盛的一頓午飯,營養又健康,那我們應該怎么來寫代碼呢?
首先,我們需要寫一個Food類,讓其他所有食物都來繼承這個類,看代碼:
代碼很簡單,我就不解釋了,然后我們寫幾個子類繼承它:
//面包類 public class Bread extends Food {private Food basic_food;public Bread(Food basic_food) {this.basic_food = basic_food;}public String make() {return basic_food.make()+"+面包";} }//奶油類 public class Cream extends Food {private Food basic_food;public Cream(Food basic_food) {this.basic_food = basic_food;}public String make() {return basic_food.make()+"+奶油";} }//蔬菜類 public class Vegetable extends Food {private Food basic_food;public Vegetable(Food basic_food) {this.basic_food = basic_food;}public String make() {return basic_food.make()+"+蔬菜";}}這幾個類都是差不多的,構造方法傳入一個Food類型的參數,然后在make方法中加入一些自己的邏輯,如果你還是看不懂為什么這么寫,不急,你看看我的Test類是怎么寫的,一看你就明白了
public class Test {public static void main(String[] args) {Food food = new Bread(new Vegetable(new Cream(new Food("香腸"))));System.out.println(food.make());} }看到沒有,一層一層封裝,我沒從里往外看:最里面我new了一個香腸,在香腸的外面我包裹了一層奶油,在奶油的外面我又加了一層蔬菜,最外面我放的是面包,是不是很形象,哈哈 ?這個設計模式簡直跟現實生活中一摸一樣,看懂了嗎??
我們看看運行結果吧?
一個三明治就做好了~~~
?
?
?
(四)適配器模式?
? 將兩種完全不同的事物聯系到一起,就像現實生活中的變壓器。假設一個手機充電器需要的電壓是20V,但是正常的電壓是220V,這時候就需要一個變壓器,將220V的電壓轉換成20V的電壓,這樣,變壓器就將20V的電壓和手機聯系起來了。
public class Test {public static void main(String[] args) {Phone phone = new Phone();VoltageAdapter adapter = new VoltageAdapter();phone.setAdapter(adapter);phone.charge();} }// 手機類 class Phone {public static final int V = 220;// 正常電壓220v,是一個常量private VoltageAdapter adapter;// 充電public void charge() {adapter.changeVoltage();}public void setAdapter(VoltageAdapter adapter) {this.adapter = adapter;} }// 變壓器 class VoltageAdapter {// 改變電壓的功能public void changeVoltage() {System.out.println("正在充電...");System.out.println("原始電壓:" + Phone.V + "V");System.out.println("經過變壓器轉換之后的電壓:" + (Phone.V - 200) + "V");} }
?
?
(五)工廠模式?
簡單工廠模式:一個抽象的接口,多個抽象接口的實現類,一個工廠類,用來實例化抽象的接口
// 抽象產品類 abstract class Car {public void run();public void stop(); }// 具體實現類 class Benz implements Car {public void run() {System.out.println("Benz開始啟動了。。。。。");}public void stop() {System.out.println("Benz停車了。。。。。");} }class Ford implements Car {public void run() {System.out.println("Ford開始啟動了。。。");}public void stop() {System.out.println("Ford停車了。。。。");} }// 工廠類 class Factory {public static Car getCarInstance(String type) {Car c = null;if ("Benz".equals(type)) {c = new Benz();}if ("Ford".equals(type)) {c = new Ford();}return c;} }public class Test {public static void main(String[] args) {Car c = Factory.getCarInstance("Benz");if (c != null) {c.run();c.stop();} else {System.out.println("造不了這種汽車。。。");}}}?
? 工廠方法模式:有四個角色,抽象工廠模式,具體工廠模式,抽象產品模式,具體產品模式。不再是由一個工廠類去實例化具體的產品,而是由抽象工廠的子類去實例化產品
?
?
// 抽象產品角色 public interface Moveable {void run(); }// 具體產品角色 public class Plane implements Moveable {@Overridepublic void run() {System.out.println("plane....");} }public class Broom implements Moveable {@Overridepublic void run() {System.out.println("broom.....");} }// 抽象工廠 public abstract class VehicleFactory {abstract Moveable create(); }// 具體工廠 public class PlaneFactory extends VehicleFactory {public Moveable create() {return new Plane();} }public class BroomFactory extends VehicleFactory {public Moveable create() {return new Broom();} }// 測試類 public class Test {public static void main(String[] args) {VehicleFactory factory = new BroomFactory();Moveable m = factory.create();m.run();} }抽象工廠模式:與工廠方法模式不同的是,工廠方法模式中的工廠只生產單一的產品,而抽象工廠模式中的工廠生產多個產品
?
/抽象工廠類 public abstract class AbstractFactory {public abstract Vehicle createVehicle();public abstract Weapon createWeapon();public abstract Food createFood(); } //具體工廠類,其中Food,Vehicle,Weapon是抽象類, public class DefaultFactory extends AbstractFactory{@Overridepublic Food createFood() {return new Apple();}@Overridepublic Vehicle createVehicle() {return new Car();}@Overridepublic Weapon createWeapon() {return new AK47();} } //測試類 public class Test {public static void main(String[] args) {AbstractFactory f = new DefaultFactory();Vehicle v = f.createVehicle();v.run();Weapon w = f.createWeapon();w.shoot();Food a = f.createFood();a.printName();} }?
?
?
(六)代理模式? (mybatis底層就是用的動態代理)
? 6-1?什么是代理模式?
? 代理模式的定義:代理模式給某一個對象提供一個代理對象,并由代理對象控制對原對象的引用。通俗的來講代理模式就是我們生活中常見的中介。
舉個例子來說明:假如說我現在想買一輛二手車,雖然我可以自己去找車源,做質量檢測等一系列的車輛過戶流程,但是這確實太浪費我得時間和精力了。我只是想買一輛車而已為什么我還要額外做這么多事呢?于是我就通過中介公司來買車,他們來給我找車源,幫我辦理車輛過戶流程,我只是負責選擇自己喜歡的車,然后付錢就可以了。用圖表示如下:
?
6-2為什么要用代理模式?(jdk的動態代理--有接口的情況? ?,CGLIB的動態代理--無接口的情況)
中介隔離作用:在某些情況下,一個客戶類不想或者不能直接引用一個委托對象,而代理類對象可以在客戶類和委托對象之間起到中介的作用,其特征是代理類和委托類實現相同的接口。
? ? ? ?開閉原則(OCP),增加功能:代理類除了是客戶類和委托類的中介之外,我們還可以通過給代理類增加額外的功能來擴展委托類的功能,這樣做我們只需要修改代理類而不需要再修改委托類,符合代碼設計的開閉原則。代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后對返回結果的處理等。代理類本身并不真正實現服務,而是通過調用委托類的相關方法,來提供特定的服務。真正的業務功能還是由委托類來實現,但是可以在業務功能執行的前后加入一些公共的服務。例如我們想給項目加入緩存、日志這些功能,我們就可以使用代理類來完成,而沒必要打開已經封裝好的委托類。(AOP 面向切面)
動態代理是在程序運行時通過反射機制動態創建的。(反射---他就像一面鏡子,可以看到你類的內部所有的東西,進而可以使用你的類)
?
6-2靜態代理??
?第一步:創建服務類接口
package main.java.proxy; public interface BuyHouse { void buyHosue(); }
?
?
?
第二步:實現服務接口
import main.java.proxy.BuyHouse; public class BuyHouseImpl implements BuyHouse { @Override public void buyHosue() { System.out.println("我要買房"); } }
?
? ??第三步:創建代理類
package main.java.proxy.impl;
import main.java.proxy.BuyHouse; 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("買房后裝修"); } }
?
? ? ??第四步:編寫測試類
?
import main.java.proxy.impl.BuyHouseImpl; import main.java.proxy.impl.BuyHouseProxy; public class ProxyTest {public static void main(String[] args) {BuyHouse buyHouse = new BuyHouseImpl();buyHouse.buyHosue();BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);buyHouseProxy.buyHosue();} }?
靜態代理總結:
優點:可以做到在符合開閉原則的情況下對目標對象進行功能擴展。
缺點:我們得為每一個服務都得創建代理類,工作量太大,不易管理。同時接口一旦發生改變,代理類也得相應修改。? ?
?
?
6-3動態代理(重點,很多框架底層都是動態代理)
?
編寫動態處理器
1 package main.java.proxy.impl;2 3 import java.lang.reflect.InvocationHandler;4 import java.lang.reflect.Method;5 6 /**7 * 8 * 9 * 10 */ 11 public class DynamicProxyHandler implements InvocationHandler { 12 13 private Object object; 14 15 public DynamicProxyHandler(final Object object) { 16 this.object = object; 17 } 18 19 @Override 20 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 21 System.out.println("買房前準備"); 22 Object result = method.invoke(object, args); 23 System.out.println("買房后裝修"); 24 return result; 25 } 26 }?
編寫測試類
1 package main.java.proxy.test;2 3 import main.java.proxy.BuyHouse;4 import main.java.proxy.impl.BuyHouseImpl;5 import main.java.proxy.impl.DynamicProxyHandler;6 7 import java.lang.reflect.Proxy;8 9 /** 10 * 11 * 12 * 13 */ 14 public class DynamicProxyTest { 15 public static void main(String[] args) { 16 BuyHouse buyHouse = new BuyHouseImpl(); 17 BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new 18 Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse)); 19 proxyBuyHouse.buyHosue(); 20 } 21 }
?
?
注意Proxy.newProxyInstance()方法接受三個參數:
- ClassLoader loader:指定當前目標對象使用的類加載器,獲取加載器的方法是固定的
- Class<?>[] interfaces:指定目標對象實現的接口的類型,使用泛型方式確認類型
- InvocationHandler:指定動態處理器,執行目標對象的方法時,會觸發事件處理器的方法
動態代理總結:雖然相對于靜態代理,動態代理大大減少了我們的開發任務,同時減少了對業務接口的依賴,降低了耦合度。但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持interface代理的桎梏,因為它的設計注定了這個遺憾。回想一下那些動態生成的代理類的繼承關系圖,它們已經注定有一個共同的父類叫Proxy。Java的繼承機制注定了這些動態代理類們無法實現對class的動態代理,原因是多繼承在Java中本質上就行不通。有很多條理由,人們可以否定對 class代理的必要性,但是同樣有一些理由,相信支持class動態代理會更美好。接口和類的劃分,本就不是很明顯,只是到了Java中才變得如此的細化。如果只從方法的聲明及是否被定義來考量,有一種兩者的混合體,它的名字叫抽象類。實現對抽象類的動態代理,相信也有其內在的價值。此外,還有一些歷史遺留的類,它們將因為沒有實現任何接口而從此與動態代理永世無緣。如此種種,不得不說是一個小小的遺憾。但是,不完美并不等于不偉大,偉大是一種本質,Java動態代理就是佐例。
?
?
CGLIB代理
JDK實現動態代理需要實現類通過接口定義業務方法,對于沒有接口的類,如何實現動態代理呢,這就需要CGLib了。CGLib采用了非常底層的字節碼技術,其原理是通過字節碼技術為一個類創建子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯。但因為采用的是繼承,所以不能對final修飾的類進行代理。JDK動態代理與CGLib動態代理均是實現Spring AOP的基礎。
1- 創建CGLIB代理類
1 package dan.proxy.impl;2 3 import net.sf.cglib.proxy.Enhancer;4 import net.sf.cglib.proxy.MethodInterceptor;5 import net.sf.cglib.proxy.MethodProxy;6 7 import java.lang.reflect.Method;8 9 /** 10 * 11 * 12 * 13 */ 14 public class CglibProxy implements MethodInterceptor { 15 private Object target; 16 public Object getInstance(final Object target) { 17 this.target = target; 18 Enhancer enhancer = new Enhancer(); 19 enhancer.setSuperclass(this.target.getClass()); 20 enhancer.setCallback(this); 21 return enhancer.create(); 22 } 23 24 public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 25 System.out.println("買房前準備"); 26 Object result = methodProxy.invoke(object, args); 27 System.out.println("買房后裝修"); 28 return result; 29 } 30 }
?
2-創建測試類
1 package dan.proxy.test;2 3 import dan.proxy.BuyHouse;4 import dan.proxy.impl.BuyHouseImpl;5 import dan.proxy.impl.CglibProxy;6 7 /**8 * 9 * 10 * 11 */ 12 public class CglibProxyTest { 13 public static void main(String[] args){ 14 BuyHouse buyHouse = new BuyHouseImpl(); 15 CglibProxy cglibProxy = new CglibProxy(); 16 BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse); 17 buyHouseCglibProxy.buyHosue(); 18 } 19 }
?
?
CGLIB代理總結:?CGLIB創建的動態代理對象比JDK創建的動態代理對象的性能更高,但是CGLIB創建代理對象時所花費的時間卻比JDK多得多。所以對于單例的對象,因為無需頻繁創建對象,用CGLIB合適,反之使用JDK方式要更為合適一些。同時由于CGLib由于是采用動態創建子類的方法,對于final修飾的方法無法進行代理。
?
?
?
?
?
?
?
?
?
? 這不是我的都市,但也有我的故事。
?
轉載于:https://www.cnblogs.com/misscai/p/9820647.html
總結
- 上一篇: 软件工程专业期末项目开发全流程模拟日志《
- 下一篇: Dreamweaver 无法显示网页小图