《设计模式》之一文带你理解建造者模式、模板方法、适配器模式、外观模式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tryandfight/article/details/83989797

我的github,到时上传例子代码
https://github.com/tihomcode


《设计模式》之一文带你理解单例、JDK动态代理、CGLIB动态代理、静态代理


建造者模式

什么是建造者模式

建造者模式:是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

工厂类模式提供的是创建单个类的模式,而建造者模式则是将各种产品集中起来进行管理,用来创建复合对象,所谓复合对象就是指某个类具有不同的属性,其实建造者模式就是前面抽象工厂模式和最后的Test结合起来得到的。

建造者模式通常包括下面几个角色

1、Builder:给出一个抽象接口,以规范产品对象的各个组成成分的建造。这个接口规定要实现复杂对象的哪些部分的创建,并不涉及具体的对象部件的创建。

2、ConcreteBuilder:实现Builder接口,针对不同的商业逻辑,具体化复杂对象的各部分的创建。 在建造过程完成后,提供产品的实例。

3、Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

4、Product:要创建的复杂对象。

建造者模式演示

public interface PersonBuilder {

    void builderHead();

    void builderBody();

    void builderFoot();

    Person builderPerson(); //组装


}
public class Person {

    private String head;
    private String body;
    private String foot;

    public String getHead() {
        return head;
    }

    public void setHead(String head) {
        this.head = head;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public String getFoot() {
        return foot;
    }

    public void setFoot(String foot) {
        this.foot = foot;
    }

}
public class JPBuilder implements PersonBuilder {
    private Person person;

    public JPBuilder() {
        person = new Person();//创建一个Person实例,用于调用set方法
    }

    public void builderHead() {
        person.setHead("日本人黑眼睛 圆脸");
    }

    public void builderBody() {
        person.setBody("体格小");
    }

    public void builderFoot() {
        person.setFoot("腿短");
    }

    public Person builderPerson() {
        return person;
    }
}

public class USBuilder implements PersonBuilder {
    private Person person;

    public USBuilder() {
        person = new Person();//创建一个Person实例,用于调用set方法
    }

    public void builderHead() {
        person.setHead("美国蓝眼睛 长脸 尖鼻");
    }

    public void builderBody() {
        person.setBody("体格大");
    }

    public void builderFoot() {
        person.setFoot("腿长");
    }

    public Person builderPerson() {
        return person;
    }

}
public class PersonDirector {
    //一定要按顺序
    public Person constructPerson(PersonBuilder pb) {
        pb.builderHead();
        pb.builderBody();
        pb.builderFoot();
        return pb.builderPerson();
    }

}
public class BuilderTest {

    public static void main(String[] args) {
        PersonDirector personDirector = new PersonDirector();
        //建造美国人
        Person person = personDirector.constructPerson(new USBuilder());
        System.out.println(person.getHead());
        System.out.println(person.getBody());
        System.out.println(person.getFoot());
        //建造日本人
        person = personDirector.constructPerson(new JPBuilder());
        System.out.println(person.getHead());
        System.out.println(person.getBody());
        System.out.println(person.getFoot());
    }
}

建造者模式应用场景

1.需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性。

2.需要生成的产品对象的属性相互依赖,需要指定其生成顺序。

3.对象的创建过程独立于创建该对象的类。在建造者模式中引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类中。

4.隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

5.Java中的StringBuilder,底层是数组(存单个字符)将字符整合在一起变成字符串的过程

建造者模式与工厂模式的区别

建造者模式更加关注与零件装配的顺序

工厂模式是将对象的全部创建过程封装在工厂类中,由工厂类向客户端提供最终的产品;而建造者模式中,建造者类一般只提供产品类中各个组件的建造,而将具体建造过程交付给导演类。由导演类负责将各个组件按照特定的规则组建为产品,然后将组建好的产品交付给客户端。简单点说,建造者模式主要负责的是部位、零件的细节实现,而装配过程让Director类来完成,但是工厂的话就是细节和装配都使用工厂类完成

建造者模式与工厂模式类似,适用的场景也很相似。一般来说,如果产品的建造很复杂,那么请用工厂模式;如果产品的建造更复杂,那么请用建造者模式。

建造者模式的优缺点

优点

建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在导演类中对整体而言可以取得比较好的稳定性。

在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

其次,建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则。

缺点

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。

如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

模板方法

什么是模板方法

模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

重复代码全部在父类里面,不同业务的,抽取给子类进行实现。抽取过程—抽象方法。

核心:处理某个流程的代码已经都具备,但是其中某个节点的代码暂时不能确定。因此,我们采用工厂方法模式,将这个节点的代码实现转移给子类完成。即:处理步骤在父类中定义好,具体的实现延迟到子类中定义。

说白了,就是将一些相同操作的代码,封装成一个算法的骨架。核心的部分留在子类中操作,在父类中只把那些骨架做好。

例如:

1.去银行办业务,银行给我们提供了一个模板就是:先取号,排对,办理业务(核心部分我们子类完成),给客服人员评分,完毕。 这里办理业务是属于子类来完成的,其他的取号,排队,评分则是一个模板。

2.去餐厅吃饭,餐厅给提供的一套模板就是:先点餐,等待,吃饭(核心部分我们子类完成),买单。这里吃饭是属于子类来完成的,其他的点餐,买单则是餐厅提供给我们客户的一个模板。

3.短信接口对接,我们项目中要使用到短信接口,假设现在我们需要的流程有

日志系统开始->发送请求->返回结果->扣款->日志系统结束

因为不同运营商他们的请求方式不一样,返回的结果也不一样,那么未加粗部分就相当于一个模板,是固定的,我们只需要将加粗部分抽象给子类去实现不同运营商的接口。

模板方法演示

下面我们就以短信运营商这个例子来写Demo

/**
 * 短信模板
 * @author TiHom
 * create at 2018/11/11 0011.
 */
public abstract class MsgTemplate {

    public void sendMsg() {
        //1.日志
        addHeadLog();
        //2.调用不同的运营商发送短信
        httpRequest();
        //3.结束日志报文
        addFootLog();
    }

    public abstract void httpRequest();

    private void addHeadLog() {
        System.out.println("调用运营商开始记录日志。。。");
    }

    private void addFootLog() {
        System.out.println("调用运营商结束记录日志。。。");
    }

}
/**
 * 中国移动接口
 * @author TiHom
 * create at 2018/11/11 0011.
 */
public class CMCC extends MsgTemplate{

    @Override
    public void httpRequest() {
        System.out.println("http://cmcc.tihom.com");
    }
}

/**
 * 中国联通接口
 * @author TiHom
 * create at 2018/11/11 0011.
 */
public class CUCC extends MsgTemplate{

    @Override
    public void httpRequest() {
        System.out.println("http://cucc.tihom.com");
    }
}

/**
 * 中国电信接口
 * @author TiHom
 * create at 2018/11/11 0011.
 */
public class CTCC extends MsgTemplate{

    @Override
    public void httpRequest() {
        System.out.println("http://ctcc.tihom.com");
    }
}
/**
 * @author TiHom
 * create at 2018/11/11 0011.
 */
public class ClientTemplate {

    public static void main(String[] args) {
        MsgTemplate cmcc = new CMCC();
        cmcc.sendMsg();
        MsgTemplate cucc = new CUCC();
        cucc.sendMsg();
        MsgTemplate ctcc = new CTCC();
        ctcc.sendMsg();
    }
}

注意交给子类处理时要用到抽象方法,在以后的设计模式中会经常见到抽象类和接口使用

模板方法应用场景

实现一些操作时,整体步骤很固定,但是呢。就是其中一小部分容易变,这时候可以使用模板方法模式,将容易变的部分抽象出来,供子类实现。

1.数据库访问的封装

2.Junit单元测试

3.servlet中关于doGet/doPost方法的调用

4.Hibernate中模板程序

5.spring中JDBCTemplate

6.HibernateTemplate

适配器

什么是适配器

在设计模式中,适配器模式(英语:adapter pattern)有时候也称包装样式或者包装(wrapper)。将一个类的接口转接成用户所期待的。一个适配使得因接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。

举个例子,比如港版的苹果和国行的苹果,港版会提供三叉头的充电头,而国行提供的是双插头的充电头,那么我们想充电的话就需要另外去买一个转接头才能使用双插头充电

适配器演示

我们就拿日本电饭煲的例子进行说明,日本电饭煲电源接口标准是110V电压,而中国标准电压接口是220V,所以要想在中国用日本电饭煲,需要一个电源转换器。

public interface CN220VInterface {

    public void connect();

}

public class CN220VInterfaceImpl implements CN220VInterface {
    public void connect() {
        System.out.println("中国220V,接通电源,开始工作");
    }
}
public interface JP110VInterface {

    //电源接口
    public void connect();
}

public class JP110VInterfaceImpl implements JP110VInterface {
    public void connect() {
        System.out.println("日本110V,接通电源,开始工作..");
    }
}
public class ElectricCooker {

    private JP110VInterface jp110VInterface;//日本电饭煲

    //这里必须传日本110V的接口进来
    ElectricCooker(JP110VInterface jp110VInterface){
        this.jp110VInterface = jp110VInterface;
    }

    public void cook(){
        jp110VInterface.connect();
        System.out.println("开始做饭了..");
    }

}
public class PowerAdaptor implements JP110VInterface {

    private CN220VInterface cn220VInterface;

    public PowerAdaptor(CN220VInterface cn220VInterface) {
        this.cn220VInterface = cn220VInterface;
    }

    public void connect() {
        cn220VInterface.connect();
    }

}

public class AdaptorTest {

    public static void main(String[] args) {
        CN220VInterface cn220VInterface = new CN220VInterfaceImpl();
        PowerAdaptor powerAdaptor = new PowerAdaptor(cn220VInterface);
        //电饭煲
        ElectricCooker cooker = new ElectricCooker(powerAdaptor);
        cooker.cook();//使用了适配器,在220V的环境可以工作了

    }
}

这里很好理解,因为电饭煲本身就是日本接口的,所以它cook方法必须是110V日本电压来做,而适配器的作用就是将220V环境变成110V,因为它最终是要适合110V,所以要实现110V接口,但是它同时要连接220V,所以引入220V。

适配器的分类

适配器分为:类适配器、对象适配、接口适配方式

类适配器方式采用继承方式,对象适配方式使用构造函数传递

适配器应用场景

1.我们在使用第三方的类库,或者说第三方的API的时候,我们通过适配器转换来满足现有系统的使用需求。

2.我们的旧系统与新系统进行集成的时候,我们发现旧系统的数据无法满足新系统的需求,那么这个时候,我们可能需要适配器,完成调用需求。

3.我们在使用不同数据库之间进行数据同步。(我这里只是分析的是通过程序来说实现的时候的情况。还有其他的很多种方式[数据库同步])

4.springmvc适配器,做请求拦截分发

5.OutputStreamWriter:是Writer的子类,将输出的字符流变为字节流,即:将一个字符流的输出对象变为字节流的输出对象。

6.InputStreamReader:是Reader的子类,将输入的字节流变为字符流,即:将一个字节流的输入对象变为字符流的输入对象。

外观模式

什么是外观模式

外观模式(Facade Pattern)门面模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。

其实就是封装。

外观模式演示

用户下单,需要调用阿里短信接口、邮件接口、微信推送接口

public interface EmailMsgService {

    public void sendMsg();
}

public class EmailMsgServiceImpl implements EmailMsgService {
    public void sendMsg() {
        System.out.println("发送邮件");
    }
}
public interface SmsMsgService {

    public void sendMsg();
}

public class SmsMsgServiceImpl implements SmsMsgService {
    public void sendMsg() {
        System.out.println("短信推送");
    }
}
public interface WechatMsgService {

    public void sendMsg();
}

public class WechatMsgServiceImpl implements WechatMsgService {
    public void sendMsg() {
        System.out.println("微信推送");
    }
}
public class Computer {

    EmailMsgService emailMsgService;
    SmsMsgService smsMsgService;
    WechatMsgService wechatMsgService;

    public Computer() {
        emailMsgService = new EmailMsgServiceImpl();
        smsMsgService = new SmsMsgServiceImpl();
        wechatMsgService = new WechatMsgServiceImpl();
    }

    public void sendMsg() {
        emailMsgService.sendMsg();
        smsMsgService.sendMsg();
        wechatMsgService.sendMsg();
    }

}
public class FacadeTest {
    public static void main(String[] args) {
        /*下面这些就是没有使用外观模式时的调用 */
//        EmailMsgService emailMsgService = new EmailMsgServiceImpl();
//        SmsMsgService smsMsgService = new SmsMsgServiceImpl();
//        WechatMsgService wechatMsgService = new WechatMsgServiceImpl();
//        emailMsgService.sendMsg();
//        smsMsgService.sendMsg();
//        wechatMsgService.sendMsg();
        /* 这是使用了外观模式后的用法,其实就是封装 */
        Computer computer = new Computer();
        computer.sendMsg();
    }
}

猜你喜欢

转载自blog.csdn.net/tryandfight/article/details/83989797