动态地给一个对象添加一些额外的职责。就增长功能来讲,装饰模式相比生成子类更为灵活。前端
动态地给一个对象添加一些额外的职责。就增长功能来讲,装饰模式相比生成子类更为灵活。java
多层装饰容易致使问题,尽可能减小装饰类的数量,以便下降系统的复杂度。web
装饰模式是对继承的有力补充。要知道继承不是万能的,继承能够解决实际的问题,可是在项目中你要考虑诸如易维护、易扩展、易复用等,并且在一些状况下要是用继承就会增长不少子类,并且灵活性很是差,那固然维护也不容易,也就是说装饰模式能够替代继承,解决咱们类膨胀的问题。同时,继承是静态地给类增长功能,而装饰模式则是动态地增长功能。redis
下面结合spring项目对装饰者模式举一个简单的例子。spring
1.Controller数据库
@RestController public class HotelController { @Resource private HotelManager hotelManager; @GetMapping("/listHotel") public String listHotel() { return JSON.toJSONString(hotelManager.listHotel()); } }
有一个controller,接收一个 listHotel 的 get 请求,调用 hotelManager (service层)的 listHotel() 接口。缓存
2.Serviceapp
public interface HotelManager { List<Hotel> listHotel(); } @Service("hotelManager") public class HotelManagerImpl implements HotelManager { @Resource private HotelDao hotelDao; @Override public List<Hotel> listHotel() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return hotelDao.listHotel(); } }
hotelManager 的 listHotel 接口直接调用 hotelDao 的 listHotel() 接口,从数据库中查询数据。这里 Thread.sleep(3000) 为了模拟从数据库查询数据返回比较慢的状况。ide
这是在java web应用开发中一个很简单的,从前端接收一个请求到数据库查询数据返回给前端的一个动做。当这个查询效率很是低,耗时很是多,可是数据又不会常常变的状况下,咱们能够经过把数据放到缓存(redis memcached等)里面来提升查询效率。memcached
若是在项目进度很是紧的状况下,咱们极可能写出下面的代码
public List<Hotel> listHotel() { if (在redis中能够查询到结果) { return redis.get(结果) } else { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return hotelDao.listHotel(); } }
首先很是丑,其次多余的代码和业务逻辑混在一块儿,让人不能一眼就看清这个接口作了什么事情。
接下来经过装饰者模式重构一下
增长一个自定义注解
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RedisCache { }
这是一个空的方法级别的注解,表示被该注解标示的方法返回的数据都应缓存在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 调用便可。