装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:
装饰器模式的应用场景:
1、需要扩展一个类的功能。
2、动态的为一个对象增加功能,而且还能动态撤销。(继承不能做到这一点,继承的功能是静态的,不能动态增删。)
缺点:产生过多相似的对象,不易排错!
实例一:
Source类是被装饰类,Decorator类是一个装饰类,可以为Source类动态的添加一些功能,代码如下:
public interface Sourceable {
public void flyMethod();
}
***************************************************
public class Source implements Sourceable {
@Override
public void flyMethod() {
System.out.println("被装饰的类:基类");
}
}
***************************************************
public abstract class Decorate implements Sourceable {
private Source source;
public Decorate() {
}
public Decorate(Source source) {
this.source = source;
}
public Source getSource() {
return source;
}
public void setSource(Source source) {
this.source = source;
}
@Override
public abstract void flyMethod();
}
***************************************************
public class FlyDecorate extends Decorate {
public FlyDecorate(Source source) {
super(source);
}
@Override
public void flyMethod() {
this.getSource().flyMethod();
this.runMethod();
}
public void runMethod() {
System.out.println("可以跑");
}
}
测试:
public class MainClass {
public static void main(String[] args) {
Source source = new Source();
Decorate decorate = new FlyDecorate(source);
decorate.flyMethod();
}
}
输出结果
被装饰的类:基类
可以跑
实例二:
Car接口 ,RunCar是被装饰类,CarDecorator装饰器抽象类或接口,FlyCarDecorator 具体的装饰器,给RunCar添加飞功能。
public interface Car {
public void show();
}
**************************************************************
基本类:被装饰的类
public class RunCar implements Car {
@Override
public void show() {
this.run();
}
public void run() {
System.out.println("可以跑");
}
}
**************************************************************
抽象装饰器:
public abstract class CarDecorator implements Car {
private Car car;
public CarDecorator(Car car) {
this.car = car;
}
public CarDecorator() {
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public abstract void show() ;
}
**************************************************************
装饰器:给RunCar添加"飞"功能
public class FlyCaDecoratorr extends CarDecorator {
public FlyCaDecoratorr(Car car) {
super(car);
}
@Override
public void show() {
this.getCar().show();
this.fly();
}
public void fly() {
System.out.println("可以飞");
}
}
**************************************************************
装饰器:给RunCar添加“游泳”功能
public class SwimCarDecorator extends CarDecorator {
public SwimCarDecorator(Car car) {
super(car);
}
@Override
public void show() {
this.getCar().show();
this.swim();
}
public void swim() {
System.out.println("可以游泳");
}
}
**************************************************************
测试:
public class MainClass {
public static void main(String[] args) {
Car runCar = new RunCar();
runCar.show();
System.out.println("***********************");
Car carDecorator = new FlyCaDecoratorr(runCar);
carDecorator.show();
System.out.println("_______________________");
Car carDecorator1 = new SwimCarDecorator(carDecorator);
carDecorator1.show();
}
}
输出结果:
可以跑
***********************
可以跑
可以飞
_______________________
可以跑
可以飞
可以游泳
实战二
项目一期开发的时候,并没有给鉴权部分设置缓存;二期开发考虑到性能问题,想要给鉴权部分加上缓存,这里就选择了使用装饰器模式进行处理;
这里使用的缓存是spring的 spring-cache,不了解没关系,知道几个注解什么意思就行
@Cacheable
表示要对方法返回值进行缓存
@CacheEvict
删除缓存注解
首先,需要一个权限的接口和实现类
public interface IDataAccessor {
/**
* 根据部门上级 id 获取所有子集部门
*/
Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds);
/**
* 获取数据范围内的部门
*/
Set<Long> deptFindScopeById(Long userId);
}
实现类(注意这里加了@Service
, 交给spring处理)
@Service public class ScopeDataAccessorImpl implements IDataAccessor { @Autowired private IDepartmentService departmentService; @Autowired private INodeScopeService nodeScopeService; @Override public Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds) { Set<Long> result = new HashSet<>(); departmentService.departmentChildren(parentIds, result); return result; } @Override public Set<Long> deptFindScopeById(Long userId) { return nodeScopeService.deptFindScopeById(userId); } }
接下来就是对之前的代码进行装饰,定义一个装饰器的实现类
(这个类没有 @Component
, 没有直接交给spring管理;加了注解会报错:找到了2个bean)
public class DataAccessorDecorator implements IDataAccessor {
private final IDataAccessor iDataAccessor;
public DataAccessorDecorator(IDataAccessor iDataAccessor) {
this.iDataAccessor = iDataAccessor;
}
@Cacheable(cacheNames = "dept:parentId", key = "#p0", sync = true)
@Override
public Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds) {
return iDataAccessor.deptFindAllChildrenByParentIds(parentIds);
}
@Cacheable(cacheNames = "dept:scope:userId", key = "#p0", sync = true)
@Override
public Set<Long> deptFindScopeById(Long userId) {
return iDataAccessor.deptFindScopeById(nodeId,userId);
}
}
接下来还需要将这个装饰器的类注册到spring中,这块使用配置文件将实例注入进去
@Configuration
@ConditionalOnBean({IDataAccessor.class})
public class Config {
@Bean
@ConditionalOnBean({IDataAccessor.class})
public DataAccessorDecorator dataAccessorDecorator(IDataAccessor iDataAccessor) {
return new DataAccessorDecorator(iDataAccessor);
}
}
根据业务,维护缓存更新;这里使用的监听部门和员工的变更事件
@Component
public class DataScopeEvict {
/**
* 清空部门相关缓存
*/
@CacheEvict(cacheNames = {"dept:parentId"}, allEntries = true)
public void department() {
}
/**
* 清空用户相关缓存
*/
@CacheEvict(cacheNames = {"dept:scope:userId"}, allEntries = true)
public void user() {
}
}
@Component
public class ScopeDataEventListener {
@Autowired
private DataScopeEvict evict;
/**
* 监听部门变更事件
*/
@EventListener
public void departmentEvent(DepartmentChangeEvent event) {
// 1 增加 2 删除 3 上级部门变更
evict.department();
}
/**
* 监听user变更事件
*/
@EventListener
public void userEvent(UserChangeEvent event) {
// 2 删除 3 主部门变更
if (event.getType().equals(2) || event.getType().equals(3)) {
evict.user();
}
}
}
一切准备就绪,使用的时候直接使用装饰器类就好了
@Service
public class UserService {
@Autowired
DataAccessorDecorator scopeDataAccessor;
public Set<Long> deptFindAllChildrenByParentIds(Collection<Long> parentIds) {
return scopeDataAccessor.deptFindAllChildrenByParentIds(parentIds);
}
public Set<Long> deptFindScopeById(Long userId) {
return scopeDataAccessor.deptFindScopeById(userId);
}
}
定义事件并发布事件
public class UserChangeEvent {
private String type;
public String getType() {
return type;
}
public UserChangeEvent setType(String type) {
this.type = type;
return this;
}
}
public class DepartmentChangeEvent {
}
@Autowired
private ApplicationContext context;//上下文,用于发布事件
void test() {
UserChangeEvent event = new UserChangeEvent();//构建事件
event.setType("3");
this.context.publishEvent(event); //发布事件
}
@Autowired ApplicationEventPublisher applicationEventPublisher;
applicationEventPublisher.publishEvent(new ErpEvent(baseId));
ApplicationEventPublisher 或者ApplicationContext都可以执行,context实现了eventPublicsher接口
实例三
咖啡+调料订单
public abstract class Drink {
protected String description;
protected float price;
public float getPrice() {
return price;
}
public Drink setPrice(float price) {
this.price = price;
return this;
}
public String getDescription() {
return description + " " + getPrice();
}
public Drink setDescription(String description) {
this.description = description;
return this;
}
// 统计金额
abstract float cost();
}
public class Decaf extends Drink{
public Decaf() {
setDescription("无因咖啡");
setPrice(0.8f);
}
@Override
float cost() {
return getPrice();
}
}
public class ShortBlack extends Drink{
public ShortBlack() {
setDescription("小特黑咖啡");
setPrice(0.5f);
}
@Override
float cost() {
return 0;
}
}
public class LongBlack extends Drink{
public LongBlack() {
setDescription("美式咖啡");
setPrice(0.1f);
}
@Override
float cost() {
return getPrice();
}
}
public class Espresso extends Drink {
public Espresso() {
setDescription("浓缩咖啡");
setPrice(0.6f);
}
@Override
float cost() {
return getPrice();
}
}
public class Decorator extends Drink {
protected Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
float cost() {
return drink.getPrice() + getPrice();
}
@Override
public String getDescription() {
return description + " " + getPrice() + "&&" + drink.getDescription();
}
}
public class Milk extends Decorator{
public Milk(Drink drink) {
super(drink);
setDescription("牛奶");
setPrice(0.7f);
}
}
public class Soy extends Decorator{
public Soy(Drink drink) {
super(drink);
setDescription("豆奶");
setPrice(0.2f);
}
}
public class Chocolate extends Decorator{
public Chocolate(Drink drink) {
super(drink);
setDescription("巧克力");
setPrice(0.4f);
}
}
public class MainClient {
public static void main(String[] args) {
// 装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
Drink drink = new LongBlack();
System.out.println("描述:"+ drink.getDescription());
System.out.println("费用:"+ drink.cost());
System.out.println("---------");
Milk milk = new Milk(drink);
System.out.println("加一份牛奶");
System.out.println("描述:"+ milk.getDescription());
System.out.println("费用:"+ milk.cost());
System.out.println("-----------");
Chocolate chocolate = new Chocolate(milk);
System.out.println("加一份巧克力");
System.out.println("描述:"+ chocolate.getDescription());
System.out.println("费用:"+ chocolate.cost());
System.out.println("-----------");
}
}
// 打印
描述:美式咖啡 0.1
费用:0.1
---------
加一份牛奶
描述:牛奶 0.7&&美式咖啡 0.1
费用:0.8
-----------
加一份巧克力
描述:巧克力 0.4&&牛奶 0.7&&美式咖啡 0.1
费用:1.1
-----------
Process finished with exit code 0