设计模式面试题(五):设计模式之模板方法模式、外观模式和原型模式

六、模板方法模式

1、什么是模板方法

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

2、什么时候使用模板方法

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

3、实际开发中应用场景哪里用到了模板方法

其实很多框架中都有用到了模板方法模式。例如: 数据库访问的封装、Junit 单元测试、servlet 中关于 doGet/doPost 方法的调用等等。

4、现实生活中的模板方法

例如:

去餐厅吃饭,餐厅给我们提供了一个模板就是: 看菜单,点菜,吃饭,付款走人 (这里 “点菜和付款” 是不确定的由子类来完成的,其他的则是一个模板。

5、代码实现模板方法模式

先定义一个模板,把模板中的点菜和付款,让子类来实现。
 

package com.lijie;

//模板方法
public abstract class RestaurantTemplate{
    // 1.看菜单
    public void menu(){
        System.out.println("看菜单");
    }

    // 2.点菜业务
    abstract void spotMenu();

    // 3.吃饭业务
    public void havingDinner(){
        System.out.println("饭"); 
    }

    // 3.付款业务
    abstract void payment();

    //4.走人
    public void GoR(){ 
        System.out.println("走人");
    }
    
    //模板通用结构
    public void process(){
        menu();
        spotMenu();
        havingDinner();
        payment();
        GoR();
    }
}

具体的模板方法子类 1

package com.lijie;

public class RestaurantGinsengImpl extends RestaurantTemplate{
    void spotMenu(){
        System.out.println("人参");
    }

    void payment(){
        System.out.println("5 快");
    }
}

具体的模板方法子类 2

package com.lijie;

public class RestaurantLobsterImpl extends RestaurantTemplate{

    void spotMenu(){
        System.out.println("龙虾");
    }

    void payment(){
        System.out.println("50块");
    }
}

1、客户端测试
 

package com.lijie;
public class Client{
    public static void main(String[] args) {
    //调用第一个模板实例
    RestaurantTemplate restaurantTemplate = new RestaurantGinsengImpl();
    restaurantTemplate.process();
    }
}

七、外观模式

1、什么是外观模式

外观模式:也叫门面模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
它向现有的系统添加一个接口,用这一个接口来隐藏实际的系统的复杂性使用外观模式,他外部看起来就是一个接口,其实他的内部有很多复杂的接口已经被实现

2、外观模式例子

用户注册完之后,需要调用阿里短信接口、邮件接口、微信推送接口。

创建阿里短信接口

package com.lijie;

//阿里短信消息
public interface AliSmsService{
    void sendSms();
}
package com.lijie;

public class AliSmsServiceImpl implements AliSmsService{

    public void sendSms(){
        System.out.println("阿里短信消息");
    }
}

1、创建邮件接口

package com.lijie;

//发送邮件消息
public interface EamilSmsService{
    void sendSms();
}
package com.lijie;

public class EamilSmsServiceImpl implements EamilSmsService{
    public void sendSms(){
        System.out.println(发送邮件消息");
    }
}

创建微信推送接口

package com.lijie;

//微信消息推送
public interface WeiXinSmsService(){
    void sendSms();
}
package com.lijie;

public class WeiXinSmsServiceImpl implements WeiXinSmsService{
    public void sendSms(){    
        System.out.println("发送微信消息推送");
    }
}

创建门面 (门面看起来很简单使用,复杂的东西以及被门面给封装好了)

package com.lijie;

public class Computer {
    AliSmsService aliSmsService;
    EamilSmsService eamilSmsService;
    WeiXinSmsService weiXinSmsService;

    public Computer(){
        aliSmsService = new AliSmsServiceImpl();
        eamilSmsService = new EamilSmsServiceImpl();
        weiXinSmsService = new WeiXinSmsServiceImpl();
    }

    //只需要调用它
    public void sendMs(){
        aliSmsService.sendSms();
        eamilSmsService.sendSms();
        weiXinSmsService.sendSms();
    }
}

启动测试

package com.lijie;

public class Client{
    public static void main(String[] args){
    //普通模式需要这样
    AliSmsService aliSmsService = new AliSmsServiceImpl();
    EamilSmsService eamilSmsService = new EamilSmsServiceImpl();
    WeiXinSmsService weiXinSmsService = newWeiXinSmsServiceImpl();
    aliSmsService.sendSms();
    eamilSmsService.sendSms();
    weiXinSmsService.sendSms();

    //利用外观模式简化方法
    new Computer().sendMsg();
    }
}

八、原型模式

1、什么是原型模式

原型设计模式简单来说就是克降原型表明了有一个样板实例,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。

2、原型模式的应用场景

  1. 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等。这时我们就可以通过原型拷贝避免这些消耗
  2. 通过 new 产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式。
  3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时可以考虑使用原型模式拷贝多个对象供调用者使用,即保护性拷贝

我们 Spring 框架中的多例就是使用原型

3、原型模式的使用方式

  1. 实现 Cloneable 接口。在 java 语言有一个 Cloneable 接口,它的作用只有一个,就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用 clone 方法。在 java 虚拟机中,只有实现了这个接口的类才可以被拷贝,否则在运行时会抛出 CloneNotSupportedException 异常。
  2. 重写 Object 类中的 clone 方法。Java 中,所有类的父类都是 Object 类Object 类中有一个 clone 方法,作用是返回对象的一个拷贝,但是其作用域protected 类型的,一般的类无法调用,因此 Prototype 类需要将 cone 方法的作用域修改为 public 类型

3.1、原型模式分为浅复制和深复制

  1. (浅复制) 只是拷贝了基本类型的数据,而引用类型数据,只是拷贝了一份引用地址。
  2. (深复制)在计算机中开辟了一块新的内存地址用于存放复制的对象

4、代码演示

创建 User 类

package com.lijie;

import java.util.ArrayList;

public class User implements Cloneable{
    private String name;
    private String password;
    private ArrayList phones;

    protected User clone(){
        try (
            User user = (User) super.clone();
            //重点,如果要连带引用类型一起复制,需要添加底下一条代码,如果不加就对于是复制了引用地址
            user.phones =(ArrayList) this.phones.clone();//设置深复制
            return user;
        }catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return null;
    }
    //省略所有属性 Git Set 方法.....
}

测试复制

package com.lijie;

import java.util.ArrayList;

public class Client{
    public static void main(String[] args){

        //创建 User 原型对象
        User user = new User();
        user.setName("李三");
        user.setPassword("123456");
        ArrayList phones = new ArrayList<>();
        phones.add("17674553302");
        user.setPhones(phones);

        //copy 一个 user 对象并且对象的属性
        User user2 = user.clone();
        user2.setPassword("654321");

        //查看俩个对象是否是一个
        System.out.println(user == user2);
    
        //查看属性内容
        System.out.println(user.getName() + "|" + user2.getName());
        System.out.println(user.getPassword() + "|" + user2.getPassword());

        //查看对于引用类型拷贝
        System.out.println(user.getPhones() == user2.getPhones());
    }
}

如果不需要深复制,需要删除 User 中的

//默认引用类型为浅复制,这是设置了深复制
user.phones = (ArrayList) this.phones.clone();

你可能感兴趣的:(设计模式,设计模式,建造者模式,模板方法模式,工厂方法模式,外观模式)