设计模式--装饰者模式
2019獨角獸企業重金招聘Python工程師標準>>>
裝飾者模式
定義
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。
通用類圖
意圖
動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更為靈活。
優點
缺點
多層裝飾容易導致問題,盡量減少裝飾類的數量,以便降低系統的復雜度。
應用場景
擴展
說明
裝飾模式是對繼承的有力補充。要知道繼承不是萬能的,繼承可以解決實際的問題,但是在項目中你要考慮諸如易維護、易擴展、易復用等,而且在一些情況下要是用繼承就會增加很多子類,而且靈活性非常差,那當然維護也不容易,也就是說裝飾模式可以替代繼承,解決我們類膨脹的問題。同時,繼承是靜態地給類增加功能,而裝飾模式則是動態地增加功能。
實踐
下面結合spring項目對裝飾者模式舉一個簡單的例子。
1.Controller
@RestController public class HotelController {@Resourceprivate HotelManager hotelManager;@GetMapping("/listHotel")public String listHotel() {return JSON.toJSONString(hotelManager.listHotel());}}有一個controller,接收一個 listHotel 的 get 請求,調用 hotelManager (service層)的 listHotel() 接口。
2.Service
public interface HotelManager {List<Hotel> listHotel();}@Service("hotelManager") public class HotelManagerImpl implements HotelManager {@Resourceprivate HotelDao hotelDao;@Overridepublic List<Hotel> listHotel() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return hotelDao.listHotel();} }hotelManager 的 listHotel 接口直接調用 hotelDao 的 listHotel() 接口,從數據庫中查詢數據。這里 Thread.sleep(3000) 為了模擬從數據庫查詢數據返回比較慢的情況。
這是在java web應用開發中一個很簡單的,從前端接收一個請求到數據庫查詢數據返回給前端的一個動作。當這個查詢效率非常低,耗時非常多,但是數據又不會經常變的情況下,我們可以通過把數據放到緩存(redis memcached等)里面來提高查詢效率。
如果在項目進度非常緊的情況下,我們很可能寫出下面的代碼
public List<Hotel> listHotel() {if (在redis中可以查詢到結果) {return redis.get(結果)} else {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return hotelDao.listHotel();}}首先非常丑,其次多余的代碼和業務邏輯混在一起,讓人不能一眼就看清這個接口做了什么事情。
接下來通過裝飾者模式重構一下
增加一個自定義注解
這是一個空的方法級別的注解,表示被該注解標示的方法返回的數據都應緩存在redis。
恢復 hotelManager.listHotel() 方法并且頭上加入自定義注解
@RedisCache @Override public List<Hotel> listHotel() {try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}return hotelDao.listHotel(); }新建一個 HotelManagerDecorate 類
public class HotelManagerDecorate {private HotelManager hotelManager;private RedisTemplate redisTemplate;public List<Hotel> listHotel() throws Exception{Method method = Util.getTarget(hotelManager).getClass().getDeclaredMethod("listHotel", null);if (method.isAnnotationPresent(RedisCache.class)) {List<Hotel> redisHotelList = (List<Hotel>) redisTemplate.opsForList().range("a", 0, -1);return Optional.ofNullable(redisHotelList).filter(list -> list.size() > 0).orElseGet(() -> {List<Hotel> hotelList = hotelManager.listHotel();redisTemplate.opsForList().leftPushAll("a", hotelList);redisTemplate.expire("a", 300, TimeUnit.SECONDS);return hotelList;});} else {return hotelManager.listHotel();}}}這是一個裝飾者類,裝飾了 hotelManager 這個類,通過反射獲取到方法上面的注解,判斷是否存在 RedisCache 這個注解,如果存在,則去redis中取得數據,否則從數據查出數據放入redis再返回。
Optional類是 java8 新增的類,有興趣的可以了解一下。
修改 HotelController 為
@GetMapping("/listHotel") public String listHotel() throws Exception{HotelManagerDecorate hotelManagerDecorate = new HotelManagerDecorate(hotelManager, redisTemplate);return JSON.toJSONString(hotelManagerDecorate.listHotel()); }修改了調用方法,由原來調用hotelManager改為調用裝飾者類,其實裝飾者類最終還是調用了hotelManager.listHotel() 方法。
這么修改之后就可以發現,再沒有修改原有代碼的基礎上,動態的給 hotelManager.listHotel() 方法增加了緩存功能,對方法的功能實現了增強,并且增強代碼與原來的業務邏輯是分離的。裝飾者只負責增強功能,業務代碼根本不知道裝飾者的存在,這樣的做法非常易于擴展和維護,具體如何調用只是由 controller 層(高層)決定。如果將來想修改一下 RedisCache 邏輯,直接在裝飾類中修改即可,根本不會影響到業務邏輯。如果將來想換一個增強的功能,直接新建一個裝飾者類,修改一下 controller 調用即可。
轉載于:https://my.oschina.net/u/232911/blog/1831790
總結
以上是生活随笔為你收集整理的设计模式--装饰者模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 编程开发之--Oracle数据库--存储
- 下一篇: 设置更改root密码 连接mysql m