设计模式8,外观模式、装饰者模式、适配器模式

目录

外观模式

装饰者模式

适配器模式


外观模式

外观模式提供了一个统一的接口,用来访问子系统中的一群接口。

类型:结构型

使用场景:当子系统越来越复杂的时候,增加外观模式可以提供简单调用接口。构建多层系统结构的时候,利用外观对象作为每层的入口,简化层间调用。

优点:简化调用过程,无需深入了解子系统,防止带来风险。减少系统以来,松散耦合。更好的划分访问层次。符合迪米特法则。

缺点:增加、扩展子系统的行为容易引入风险。不符合开闭原则。

例子:

public class Goods {
    private String name;

    public Goods(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
public class OrderService {
    public boolean isAvailable(Goods goods){
        System.out.println("商品:" + goods.getName() + ",库存校验通过,下单成功");
        return true;
    }
}
public class PaymentService {
    public boolean pay(Goods goods){
        System.out.println("支付" + goods.getName() + "成功");
        return true;
    }
}
public class ShippingService {
    public String shipGoods(Goods goods){
        System.out.println(goods + "进入物流系统");
        String orderNum = "101";
        return orderNum;
    }
}
public class StoreService {
    private OrderService orderService;
    private PaymentService paymentService;
    private ShippingService shippingService;

    public void setOrderService(OrderService orderService) {
        this.orderService = orderService;
    }

    public void setPaymentService(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void setShippingService(ShippingService shippingService) {
        this.shippingService = shippingService;
    }

    public void goodsSell(Goods goods){
        if(orderService.isAvailable(goods)){
            if(paymentService.pay(goods)){
                String shippingNum = shippingService.shipGoods(goods);
                System.out.println("下单成功,订单号:" + shippingNum);
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Goods goods = new Goods("衣服");
        StoreService storeService = new StoreService();

        storeService.setOrderService(new OrderService());
        storeService.setPaymentService(new PaymentService());
        storeService.setShippingService(new ShippingService());

        storeService.goodsSell(goods);
    }
}

看下类图

事实上这种方式并不符合外观模式,对于外观模式,应用层只需要和StoreService关联就好,不需要跟其他Service关联,而这里在应用层也创建了其他service,所以需要调整一下代码

public class StoreService {
    private OrderService orderService = new OrderService();
    private PaymentService paymentService = new PaymentService();
    private ShippingService shippingService = new ShippingService();

    public void goodsSell(Goods goods){
        if(orderService.isAvailable(goods)){
            if(paymentService.pay(goods)){
                String shippingNum = shippingService.shipGoods(goods);
                System.out.println("下单成功,订单号:" + shippingNum);
            }
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Goods goods = new Goods("衣服");
        StoreService storeService = new StoreService();
        storeService.goodsSell(goods);
    }
}

再看类图

这样才符合外观模式。

用到设计模式的源码:

Configuration#newMetaObject主要看其被调用的方法,调用方法想要一个MetaObject,只需要调Configuration这个方法就好,而不需要直接和MetaObject有关系。


装饰者模式

在不改变原油对象的基础上,将功能附加到对象上。提供了比继承更有弹性的替代方案(扩展原有对象功能)。

我们通常使用继承进行扩展,但如果扩展的功能种类繁多,那就会生成很多子类来增加系统的复杂性。并且使用继承扩展,必须可预见这些功能。

类型:结构型

使用场景:扩展一个类的功能或给一个类添加附加职责。动态给一个兑现更添加功能,这些功能可以再动态撤销。

优点:是继承的有力补充,比继承灵活,不改变原有对象的情况下给一个对象扩展功能。通过使用不同的装饰类以及这些装饰类的排列组合,可以实现不同的效果。符合开闭原则。

缺点:会出现更多代码,更多的对象,增加系统复杂性。动态或多层装饰会更复杂。

例子:

public class Cake {
    protected String getName(){
        return "蛋糕";
    }
    protected int price(){
        return 10;
    }
}
public class MangoCake extends Cake{
    @Override
    public String getName() {
        return "芒果" + super.getName();
    }

    @Override
    public int price() {
        return super.price() + 5;
    }
}
public class MangoAppleCake extends MangoCake {
    @Override
    public String getName() {
        return "苹果" + super.getName();
    }

    @Override
    public int price() {
        return super.price() + 6;
    }
}
public class Test {
    public static void main(String[] args) {
        Cake cake = new Cake();
        System.out.println(cake.getName() + ",价格:" + cake.price());

        cake = new MangoCake();
        System.out.println(cake.getName() + ",价格:" + cake.price());

        cake = new MangoAppleCake();
        System.out.println(cake.getName() + ",价格:" + cake.price());
    }
}

 这种写法对于扩展来说非常困难,蛋糕有各种各样的,如果我还想要别的种类,那还要加更多的类,更麻烦,于是用装饰者模式试试:

public abstract class AbstractCake {
    protected abstract String getName();
    protected abstract int price();
}
public class AbstractDecorator extends AbstractCake {
    private AbstractCake aCake;

    public AbstractDecorator(AbstractCake aCake) {
        this.aCake = aCake;
    }

    @Override
    protected String getName() {
        return this.aCake.getName();
    }

    @Override
    protected int price() {
        return this.aCake.price();
    }
}
public class Cake extends AbstractCake {
    protected String getName(){
        return "蛋糕";
    }
    protected int price(){
        return 10;
    }
}
public class Apple extends AbstractDecorator {
    public Apple(AbstractCake aCake) {
        super(aCake);
    }

    @Override
    protected String getName() {
        return "苹果" + super.getName();
    }

    @Override
    protected int price() {
        return super.price() + 6;
    }
}
public class Mango extends AbstractDecorator{
    public Mango(AbstractCake aCake) {
        super(aCake);
    }

    @Override
    public String getName() {
        return "芒果" + super.getName();
    }

    @Override
    public int price() {
        return super.price() + 5;
    }
}
public class Test {
    public static void main(String[] args) {
        AbstractCake cake = new Cake();
        cake = new Mango(cake);
        cake = new Mango(cake);
        cake = new Apple(cake);
        System.out.println(cake.getName() + ",价格:" + cake.price());
    }
}

再看下类图:

用这种方式,可以很自由的选择加什么东西,扩展起来也更方便。

 用到设计模式的源码:

BufferedReader继承Reader,并且用in来构建BufferedReader

ServletRequest

直接看类图:

mybatis中的Cache

 上面decorators中的类都是cache的装饰类,可以自己去看看。


适配器模式

将一个类的接口(被适配者)转换成客户期望的另一个接口(目标)。使原本不兼容的类可以一起工作。

类型:结构型

使用场景:已经存在的类的方法和需求不匹配时(方法和结果相同或相似)。不是软件设计阶段考虑的设计模式,而是随着软件维护,由于不同产品、不同厂家造成功能类似而接口不相同的情况下的解决方案。

优点:能提高类的透明性和复用性,现有的类复用但不需要改变。目标类和适配器类解耦,提高程序的扩展性。符合开闭原则。

缺点:在编写过程中要全面考虑,可能会在增加系统的复杂性。增加系统代码的可读难度。

例子:

类适配器模式:通过集成获取被适配者的方法,并修改为自己的实现。

public class Adaptee {
    public void adapteeRequest(){
        System.out.println("被适配者的方法");
    }
}
public interface Target {
    void request();
}
public class ConcreteTarget implements Target {
    @Override
    public void request() {
        System.out.println("ConcreteTarget目标方法");
    }
}
public class Adapter extends Adaptee implements Target {
    @Override
    public void request() {
        super.adapteeRequest();
    }
}
public class Test {
    public static void main(String[] args) {
        Target target = new ConcreteTarget();
        target.request();

        Target adapterTarget = new Adapter();
        adapterTarget.request();
    }

}

 类图:

目标方法通过Target的接口,Target通过Adapter移交给了被适配者(Adaptee)

对象适配器模式

一个场景:电源适配器,从220v交流电转换为手机可用的5v直流电

public class AC220 {
    public int outputAC220V(){
        int output = 220;
        System.out.println("输入交流电" + output + "V");
        return output;
    }
}
public interface DC5 {
    int outputDC5V();
}
public class PowerAdapter implements DC5 {
    private AC220 ac220 = new AC220();

    @Override
    public int outputDC5V() {
        int adapterInput = ac220.outputAC220V();
        int adapterOutput = adapterInput / 44;
        System.out.println("使用适配器,输入" + adapterInput + ",输出" + adapterOutput);
        return adapterOutput;
    }
}
public class Test {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter();
        dc5.outputDC5V();
    }
}

类图:

 用到设计模式的源码:

例如XmlAdapter

xml读取的一个抽象类,用的时候继承这个类并实现相关接口

其实适配器模式在spring启动配置时经常用到,我们在之前springboot的源码分析中也可以见到大量的Adapter,DispatcherServlet#doDispatch中

因为会传入若干handler,那也有若干适配器与他匹配,适配器实现HandlerAdapter接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值