设计模式之结构模式(一)

版权声明:欢迎转载评论~哈哈哈哈请标明出处呀 https://blog.csdn.net/legendaryhaha/article/details/88541080


前言

在Java体系结构与设计模式一书中,是这样描述结构模式的:把责任委托给其他类的对象,从而引入一种耦合度低的分层体系结构。这种结构带来的好处就是(1)方便对象间的通信,比如,当某个对象以通常的方式无法访问,或因接口不兼容导致某个对象不可用时。(2)另外,结构模式提供了组织聚合对象的方式,从而使其能完整地被创建;并且结构模式还提供了及时回收系统资源的方式。

在这里插入图片描述


装饰器

装饰器将目标对象拓展的功能封装在另一个一个类中,接着,客户端的请求转发到目标对象之前,先经过装饰器,然后,装饰器再将这些请求转给目标对象(之前或之后)时,动态的将拓展的功能加入目标对象。这种动态地扩展对象功能的方式,不需要我们改变原始的代码或使用继承。
在这里插入图片描述
为了达到上述设计,我们进行程序设计时,则需要满足以下的条件:(1)装饰器需要拥有与底层对象相同接口,这是为了客户对象可以像和底层对象交互的方式那样,同装饰器进行交互。(2)装饰器对象需要包含实际对象的引用,因为装饰器对象接受来自客户对象的所有请求(调用)。然后,将这些调用转发给底层对象,此时,就需要一个东西指引装饰器找到目标对象。

(1)首先,我们举一个书上的例子:有一个日志打印程序,打印的方式有两种,第一种直接打印到控制台,第二种直接打印到文件。于是有了如下的UML图:

在这里插入图片描述
现在,我们想拓展两个功能,一个是对日志文件加密功能,一个是将日志转为HTML然后再打印。在不改变原来的代码的基础上,进行功能拓展,通过继承无疑是较好的方式,于是有了如下的类图:
在这里插入图片描述
从图中,我们可以直观的看出,直接通过继承来扩展功能的方法的灵活性是比较差的,在某些场合下,甚至会导致大量子类的产生,维护性和可读性都将大大下降。于是,装饰器模式提出了如下的解决办法:
在这里插入图片描述
LoggerDecorator类实现Logger接口,这样保证了拓展的功能和原来的功能,依然具有共同的接口,便于类型的转换。拓展的功能HTMLLogger类和EncryptLogger类继承LoggerDecorator类,重写log方法,从而实现拓展的功能。

注意:LoggerDe corator类持有接口的引用logger,这样,在客户端不管实例化ConsoleLogger打印到控制台类还是实例化打印到文件的FileLogger类,都可以向上转型为接口。

代码:

package top.fang.decorator;

/**
 * 公共的打印接口
 */
public interface Log {
    void log(String msg);
}

package top.fang.decorator;

/**
 * 实现打印机口的两个具体打印方法
 * 打印到文件
 * 打印到控制台
 */
public class FileLogImpl implements Log {
    @Override
    public void log(String msg) {
        System.out.println("===打印到文件当中===");
        System.out.println(msg);
    }
}

package top.fang.decorator;

public class ConsoleLogImpl implements Log {
    @Override
    public void log(String msg) {
        System.out.println("====打印到控制台====");
        System.out.println(msg);
    }
}

package top.fang.decorator;

public abstract class DecoratorLogImpl implements Log {
    protected Log log;

    public DecoratorLogImpl(Log log){
        super();
        this.log = log;
    }

}
package top.fang.decorator;

/**
 * 拓展功能的两个类,持有Log接口的引用,根据客户端实例化的具体对象,自动调用相应的类
 * 转为HTML
 * 消息加密
 */
public class HTMLFileExtendsDecoratorLogImpl extends DecoratorLogImpl {

    public HTMLFileExtendsDecoratorLogImpl(Log log){
        super(log);
    }

    @Override
    /**
     * log 为抽象父类的属性
     */
    public void log(String msg) {
        String htmlMsg = "传入的消息已转为HTML";
        log.log(htmlMsg);
    }


}
package top.fang.decorator;

public class EncryLogExtendsDecoratorLogImpl extends DecoratorLogImpl {
    public EncryLogExtendsDecoratorLogImpl(Log log){
        super(log);
    }
    @Override
    public void log(String msg) {
        String encryMsg = "传入的消息已经加密";
        log.log(encryMsg);
    }
}

测试

package top.fang.decorator;

public class Cilent {
    public static void main(String[] args) {
        /**
         * 这里我们还可以用工厂的方法在造实例对象
         */
        HTMLFileExtendsDecoratorLogImpl test =
                new HTMLFileExtendsDecoratorLogImpl(new FileLogImpl());
        test.log("天上掉馅饼了");
    }
}


适配器

类适配器

适配器是用于接口转换的,但注意此接口不是Java中定义的接口那样,它是一个类里对外提供外面对象访问的方法。加入有一个interface(为了区别开来,实际意义上的接口用英文表示)它提供的方法名是isValid(),并且在这个方法下也有对个实现者了,现在在拓展业务的过程中,有个类单独命名了这样的一个方法isChinaVaild(),那么问题来了,在客户端方面,我们都是依赖interface来访问具体业务里的isValid方法的,但现在我们做不到依赖这个interface访问到isChinaValid方法了:

public interface Test{
     void isValid();
}
public USA implements  Test{
     void isValid{
     //实现
     }
}
//客户端
Test test =new USA()//或者通过工厂获得对象
test.isValid()  //只依赖接口就能访问USA的isValid方法
//现在拓展了中国业务,由于具体需求,我们的方法名变了,还有返回值,此时不能通过上面的方式访问了
public China implements Test{
    String isChinaValid(){
    //实现
    }
}

根据上面例子,我们的适配器模式提出了一种解决办法,增加一个类继承China类并实现Test接口,然后在isValid方法中完成对isChianV alid方法的调用,即将拥有独立业务的China类适配到Test接口。

public class ChinaAdapter extends China implements Test{
		void isValid(){
		     isChinaValid();
		}
}

就这样添加一个类,就完成了适配,这种模式非常常见,如果了解Java的组件的话,肯定不陌生,它在组件中就定义了很多适配器,譬如鼠标点击,键盘输入都有相应适配器来完成具体的事件类到监听类接口的转换。

对象适配器

上面说的例子中,适配器除了继承适配源(China)之外,还要实现接口Test,但如果Test不再是一个接口而是一个抽象类,那么由于Java只支持单继承(除了接口),则不能采用上面的模式进行设计了,于是便有了对象适配器。

此时,我们只能选择继承抽象类,然后在子类中持有适配源对象(China)

abstract class Test{
     void isValid();
}

class ChinaAdapter extends Test{
		private Chian china;
		public Test(China china){
		      this.china  =  china;
		}
		void isValid{
		   china.isChinaValid();
		}
}

对比这两种方式,后者显得更加灵活,但也有缺点,因为构造适配器对象时,需要传入一个适配源对象(China),这样客户端就知道它存在的事实了。


责任链

责任链模式在如今的设计中也是常用的,并有相应的框架进行支持,譬如JBPM,工作流引擎。它的设计思想如下:假如有一个请求,它的潜在处理对象有A,B,C,D,此时,我们可以对A、B、C、D以一种顺序进行排序,然后请求按照这个顺序逐层走。对于接受到请求的A,B,C,D,它们只负责做这样的决定:进行处理或者传递给下一个对象,不负责下一个能不能做。

典型的应用就是一个公司,不同级别的人,对某个项目批准的金额是有上限的,十万可能是经理级别,100万需要董事长批准了。

package responsibility;

public abstract class Requesthandler {
    private Requesthandler nextHandler;

    public Requesthandler getNextHandler() {
        return nextHandler;
    }
    //设置下一个传递对象
    public void setNextHandler(Requesthandler nextHandler) {
        this.nextHandler = nextHandler;
    }
    //关键方法,不同的领导具体实现该方法
    public abstract boolean handle(Requester requester);
}

class Boss extends Requesthandler{
    private static int LIMIT = 1000000;

    @Override
    public boolean handle(Requester requester) {
        if(requester.getMoney()<LIMIT){
            System.out.println("boss可以处理");
            return true;
        }else{
            return getNextHandler().handle(requester);
        }
    }
}
class Manager extends Requesthandler{
    private static int LIMIT = 100000;

    @Override
    public boolean handle(Requester requester) {
        if(requester.getMoney()<LIMIT){
            System.out.println("经理可以处理");
            return true;
        }
        System.out.println("经理不可以处理传至上一级");
        return getNextHandler().handle(requester);
    }
}
public class Requester{
    private String id;
    private int money;

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}
class Client{
    private Boss boss;
    private Manager manager;
    public static void main(String[] args) {
        Client client = new Client();
        client.createWorkFlow();//创造一个工作流
        Requester requester = new Requester();
        requester.setId("项目001");
        requester.setMoney(200000);//20万
        client.manager.handle(requester);

    }

    private void createWorkFlow() {
        boss = new Boss();//boss为最上级
        manager = new Manager();
        manager.setNextHandler(boss);//设置经理的上一级为boss

    }
}

// console
经理不可以处理传至上一级
boss可以处理

上面的例子中,我们定义了一个handle类,它定义下一级要传递的对象的方法,还有一个抽象方法,表示各个级别的领导相应的权利,各个领导类继承该类,并具体化该方法,最后在客户端中,我们创建一个工作流,最低级的经理设置上级为boss,boss做最后的处理。

通过以上模式,我们避免了将请求同时绑定在经理和boss上,从而降低请求和一系列潜在的处理对象之间的耦合。

猜你喜欢

转载自blog.csdn.net/legendaryhaha/article/details/88541080