设计模式:Decorator--装饰器模式

装饰模式就是给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例,关系图如下:

装饰器模式的应用场景:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值