記錄自己瞭解的一些設計模式,記錄自己設計模式
單例模式
單例模式比較好理解,Spring就是典型的例子。被Spring中的容器管理的對象都有對應的scope,配置成singleton說明這個對象就是單例,也就是在Spring容器的生命週期中,這個類只有1個執行個體。
java中單例模式的寫法也有好多種。比如懶漢式、餓漢式、內部類方式、枚舉方式等。
單例模式實現時設計到JMM和volatile關鍵字,詳見:單例模式
需要注意的如果使用dcl的話需要初始化過程,這篇Java記憶體模型之從JMM角度分析DCL文章中說明了dcl的正確用法。
Effectice java中推薦的單例方式寫法是使用枚舉類型的方式。
原廠模式
原廠模式的意義在於對象的建立、管理可以使用工廠去管理,而不是建立者自身。最典型的原廠模式使用者就是Spring,Spring內部的容器就是一個工廠,所有的bean都由這個容器管理,包括它們的建立、銷毀、注入都被這個容器管理。
原廠模式分Factory 方法(普通工廠、多個工廠、靜態工廠)和抽象工廠。它們的區別在於抽象工廠抽象程度更高,把工廠也抽象成了一個介面,這樣可以再每添加一個新的對象的時候而不需要修改工廠的代碼。
比如有個Repository介面,用於儲存資料,有DatabaseRepository,CacheRepository,FileRepository分別在資料庫,緩衝,檔案中儲存資料,定義如下:
public interface Repository { void save(Object obj);} class DatabaseRepository implements Repository { @Override public void save(Object obj) { System.out.println("save in database"); }}class CacheRepository implements Repository { @Override public void save(Object obj) { System.out.println("save in cache"); }}class FileRepository implements Repository { @Override public void save(Object obj) { System.out.println("save in file"); }}
簡單工廠的使用
public class RepositoryFactory { public Repository create(String type) { Repository repository = null; switch (type) { case "db": repository = new DatabaseRepository(); break; case "cache": repository = new CacheRepository(); break; case "file": repository = new FileRepository(); break; } return repository; } public static void main(String[] args) { RepositoryFactory factory = new RepositoryFactory(); factory.create("db").save(new Object()); factory.create("cache").save(new Object()); factory.create("file").save(new Object()); }}
簡單工廠的弊端在於每添加一個新的Repository,都必須修改RepositoryFactory中的代碼
抽象工廠的使用
public interface RepositoryFactoryProvider { Repository create();} class DatabaseRepositoryFactory implements RepositoryFactoryProvider { @Override public Repository create() { return new DatabaseRepository(); }}class CacheRepositoryFactory implements RepositoryFactoryProvider { @Override public Repository create() { return new CacheRepository(); }}class FileRepositoryFactory implements RepositoryFactoryProvider { @Override public Repository create() { return new FileRepository(); }}
抽象工廠的測試:
RepositoryFactoryProvider dbProvider = new DatabaseRepositoryFactory();dbProvider.create().save(new Object());RepositoryFactoryProvider cacheProvider = new CacheRepositoryFactory();cacheProvider.create().save(new Object());RepositoryFactoryProvider fileProvider = new FileRepositoryFactory();fileProvider.create().save(new Object());
抽象工廠把工廠也進行了抽象話,所以添加一個新的Repository的話,只需要新增一個RepositoryFactory即可,原有代碼不需要修改。
代理模式
代理模式的作用是使用一個代理類來代替原先類進行操作。比較常見的就是aop中就是使用代理模式完成事務的處理。
代理模式分靜態代理和動態代理,靜態代理的原理就是對目標對象進行封裝,最後調用目標對象的方法即可。靜態代理和動態代理區別:待補充
動態代理跟靜態代理的區別就是動態代理中的代理類是程式啟動並執行時候產生的。Spring中對於介面的代理使用jdk內建的Proxy和InvocationHandler實現,對於類的代理使用cglib完成。
以1個UserService為例,使用jdk內建的代理模式完成計算方法調用時間的需求:
// UserService介面public interface IUserService { void printAll();}// UserService實作類別class UserService implements IUserService { @Override public void printAll() { System.out.println("print all users"); }}// InvocationHandler策略,這裡列印了方法調用前後的時間@AllArgsConstructorclass UserInvocationHandler implements InvocationHandler { private IUserService userService; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start : " + System.currentTimeMillis()); Object result = method.invoke(userService, args); System.out.println("end : " + System.currentTimeMillis()); return result; }}
測試:
IUserService userService = new UserService();UserInvocationHandler uih = new UserInvocationHandler(userService);IUserService proxy = (IUserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), new Class[] {IUserService.class}, uih);proxy.printAll(); // 列印出start : 1489665566456 print all users end : 1489665566457
觀察者設計模式
觀察者設計模式主要的使用情境在於一個對象變化之後,依賴該對象的對象會收到通知。典型的例子就是rss的訂閱,當訂閱了部落格的rss之後,當部落格更新之後,訂閱者就會收到新的訂閱資訊。
jdk內建提供了Observable和Observer,用來實現觀察者模式:
// 定義一個Observablepublic class MetricsObserable extends Observable { private Map<String, Long> counterMap = new HashMap<>(); public void updateCounter(String key, Long value) { counterMap.put(key, value); setChanged(); notifyObservers(counterMap); }}// Observerpublic class AdminA implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("adminA: " + arg); }}public class AdminB implements Observer { @Override public void update(Observable o, Object arg) { System.out.println("adminB: " + arg); }}
測試:
MetricsObserable metricsObserable = new MetricsObserable();metricsObserable.addObserver(new AdminA());metricsObserable.addObserver(new AdminB());metricsObserable.updateCounter("request-count", 100l);
列印:
adminB: {request-count=100}adminA: {request-count=100}
適配器模式
適配器模式比較好理解。像生活中插線口的插頭有2個口的,也有3個口的。如果電腦的電源插口只有3個口的,但是我們需要一個2個口的插口的話,這個時候就需要使用插座來外接這個3個口的插頭,插座上有2個口的插頭。
這個例子跟我們編程一樣,當使用者系統的介面跟我們系統內部的介面不一致時,我們可以使用適配器來完成介面的轉換。
使用繼承的方式實作類別的適配:
public class Source { public void method() { System.out.println("source method"); }}interface Targetable { void method(); void newMethod();}class Adapter extends Source implements Targetable { @Override public void newMethod() { System.out.println("new method"); }}
測試:
Targetable targetable = new Adapter();targetable.method(); // source methodtargetable.newMethod(); // new method
上述方式是用介面和繼承的方式實現適配器模式。當然我們也可以使用組合的方式實現(把Source當成屬性放到Adapter中)。
在java流、檔案使用到了這兩個模式:
位元組流:InputStream、OutputStream
字元流:Reader、Writer
1、適配器模式
FileInputStream fileinput=new FileInputStream(file);//位元組流,讀一個位元組
InputStreamReader inputStreamReader=new InputStreamReader(fileinput);//字元流,讀一個字元
2、裝飾者模式
BufferedReader bufferedReader=new BufferedReader(inputStreamReader);//讀一行字元
享元模式
線程池中會構造幾個核心線程用於處理,這些線程會去取阻塞隊列裡的任務然後進行執行。這些線程就是會被共用、且被重複使用的。因為線程的建立、銷毀、調度都是需要消耗資源的,沒有必要每次建立新的線程,而是共用一些線程。這就是享元模式的使用。類似的還有jdbc串連池,對象池等。
之前有一次面試被問到:
Integer.valueOf("1") == Integer.valueOf("1") // true還是false
當時回答的是false,後來翻了下Integer的源碼發現Integer裡面有個內部類IntegerCache,用於緩衝一些共用的Integer。這個緩衝的範圍可以在jvm啟動的時候進行設定。
註:-128到127都相等,128之後不等。見裝箱、拆箱知識點。
其實後來想想也應該這麼做,我們沒有必要每次使用對象的時候都返回新的對象,可以共用這些對象,因為新對象的建立都是需要消耗記憶體的。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。 http://blog.csdn.net/wwwtotoro/article/details/79547383