Java 设计模式 — 外观 · 原型 策略 · 观察者
📋 本文节选自《Java 设计模式面试题》,返回 查看全部章节
10.外观模式
1.什么是外观模式
- 外观模式:也叫门面模式,隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。
- 它向现有的系统添加一个接口,用这一个接口来隐藏实际的系统的复杂性。
- 使用外观模式,他外部看起来就是一个接口,其实他的内部有很多复杂的接口已经被实现
2.外观模式例子
- 用户注册完之后,需要调用阿里短信接口、邮件接口、微信推送接口。
1、 创建阿里短信接口;
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("发送邮件消息");
}
}1、 创建微信推送接口;
package com.lijie;
//微信消息推送
public interface WeiXinSmsService {
void sendSms();
}package com.lijie;
public class WeiXinSmsServiceImpl implements WeiXinSmsService {
public void sendSms() {
System.out.println("发送微信消息推送");
}
}1、 创建门面(门面看起来很简单使用,复杂的东西以及被门面给封装好了);
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 sendMsg() {
aliSmsService.sendSms();
eamilSmsService.sendSms();
weiXinSmsService.sendSms();
}
}1、 启动测试;
package com.lijie;
public class Client {
public static void main(String[] args) {
//普通模式需要这样
AliSmsService aliSmsService = new AliSmsServiceImpl();
EamilSmsService eamilSmsService = new EamilSmsServiceImpl();
WeiXinSmsService weiXinSmsService = new WeiXinSmsServiceImpl();
aliSmsService.sendSms();
eamilSmsService.sendSms();
weiXinSmsService.sendSms();
//利用外观模式简化方法
new Computer().sendMsg();
}
}11.原型模式
1.什么是原型模式
- 原型设计模式简单来说就是克隆
- 原型表明了有一个样板实例,这个原型是可定制的。原型模式多用于创建复杂的或者构造耗时的实例,因为这种情况下,复制一个已经存在的实例可使程序运行更高效。
2.原型模式的应用场景
1、 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等这时我们就可以通过原型拷贝; 避免这些消耗。 2、 通过new产生的一个对象需要非常繁琐的数据准备或者权限,这时可以使用原型模式; 3、 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模; 式拷贝多个对象供调用者使用,即保护性拷贝。
我们Spring框架中的多例就是使用原型。
3.原型模式的使用方式
1、 实现Cloneable接口在java语言有一个Cloneable接口,它的作用只有一个,就是在运行时通知; 虚拟机可以安全地在实现了此接口的类上使用clone方法。在java虚拟机中,只有实现了这个接口 的类才可以被拷贝,否则在运行时会抛出CloneNotSupportedException异常。 2、 重写Object类中的clone方法Java中,所有类的父类都是Object类,Object类中有一个clone方; 法,作用是返回对象的一个拷贝,但是其作用域protected类型的,一般的类无法调用,因此 Prototype类需要将clone方法的作用域修改为public类型。
3.1 原型模式分为浅复制和深复制
1、 (浅复制)只是拷贝了基本类型的数据,而引用类型数据,只是拷贝了一份引用地址; 2、 (深复制)在计算机中开辟了一块新的内存地址用于存放复制的对象;
4.代码演示
1、 创建User类;
package com.lijie;
import java.util.ArrayList;
public class User implements Cloneable {
private String name;
private String password;
private ArrayList<String> phones;
protected User clone() {
try {
User user = (User) super.clone();
//重点,如果要连带引用类型一起复制,需要添加底下一条代码,如果不加就对于是复制了引用地址
user.phones = (ArrayList<String>) this.phones.clone();//设置深复制
return user;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
//省略所有属性Git Set方法......
}1、 测试复制;
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<String> 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());
}
}1、 如果不需要深复制,需要删除User中的;
//默认引用类型为浅复制,这是设置了深复制
user.phones = (ArrayList<String>) this.phones.clone();12.策略模式
1.什么是策略模式
- 定义了一系列的算法 或 逻辑 或 相同意义的操作,并将每一个算法、逻辑、操作封装起来,而且使它们还可以相互替换。(其实策略模式Java中用的非常非常广泛)
- 我觉得主要是为了 简化 if…else 所带来的复杂和难以维护。
2.策略模式应用场景
- 策略模式的用意是针对一组算法或逻辑,将每一个算法或逻辑封装到具有共同接口的独立的类中, 从而使得它们之间可以相互替换。
1、 例如:我要做一个不同会员打折力度不同的三种策略,初级会员,中级会员,高级会员(三种不同; 的计算)。 2、 例如:我要一个支付模块,我要有微信支付、支付宝支付、银联支付等;
3.策略模式的优点和缺点
- 优点:
1、算法可以自由切换。
2、避免使用多重条件判断。
3 、扩展性非常良好。
- 缺点:
1、策略类会增多。
2、所有策略类都需要对外暴露。
4.代码演示
- 模拟支付模块有微信支付、支付宝支付、银联支付
1、 定义抽象的公共方法;
package com.lijie;
//策略模式 定义抽象方法 所有支持公共接口
abstract class PayStrategy {
// 支付逻辑方法
abstract void algorithmInterface();
}1、 定义实现微信支付;
package com.lijie;
class PayStrategyA extends PayStrategy {
void algorithmInterface() {
System.out.println("微信支付");
}
}1、 定义实现支付宝支付;
package com.lijie;
class PayStrategyB extends PayStrategy {
void algorithmInterface() {
System.out.println("支付宝支付");
}
}1、 定义实现银联支付;
package com.lijie;
class PayStrategyC extends PayStrategy {
void algorithmInterface() {
System.out.println("银联支付");
}
}1、 定义下文维护算法策略;
package com.lijie;// 使用上下文维护算法策略
class Context {
PayStrategy strategy;
public Context(PayStrategy strategy) {
this.strategy = strategy;
}
public void algorithmInterface() {
strategy.algorithmInterface();
}
}1、 运行测试;
package com.lijie;
class ClientTestStrategy {
public static void main(String[] args) {
Context context;
//使用支付逻辑A
context = new Context(new PayStrategyA());
context.algorithmInterface();
//使用支付逻辑B
context = new Context(new PayStrategyB());
context.algorithmInterface();
//使用支付逻辑C
context = new Context(new PayStrategyC());
context.algorithmInterface();
}
}13.观察者模式
1. 什么是观察者模式
- 先讲什么是行为性模型,行为型模式关注的是系统中对象之间的相互交互,解决系统在运行时对象 之间的相互通信和协作,进一步明确对象的职责。
- 观察者模式,是一种行为性模型,又叫发布-订阅模式,他定义对象之间一种一对多的依赖关系, 使得当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。
2. 模式的职责
- 观察者模式主要用于1对N的通知。当一个对象的状态变化时,他需要及时告知一系列对象,令他 们做出相应。
实现有两种方式:
1、 推:每次都会把通知以广播的方式发送给所有观察者,所有的观察者只能被动接收; 2、 拉:观察者只要知道有情况即可,至于什么时候获取内容,获取什么内容,都可以自主决定;
3. 观察者模式应用场景
1、 关联行为场景,需要注意的是,关联行为是可拆分的,而不是“组合”关系事件多级触发场景; 2、 跨系统的消息交换场景,如消息队列、事件总线的处理机制;
4.代码实现观察者模式
1、 定义抽象观察者,每一个实现该接口的实现类都是具体观察者;
package com.lijie;
//观察者的接口,用来存放观察者共有方法
public interface Observer {
// 观察者方法
void update(int state);
}1、 定义具体观察者;
package com.lijie;
// 具体观察者
public class ObserverImpl implements Observer {
// 具体观察者的属性
private int myState;
public void update(int state) {
myState=state;
System.out.println("收到消息,myState值改为:"+state);
}
public int getMyState() {
return myState;
}
}1、 定义主题主题定义观察者数组,并实现增、删及通知操作;
package com.lijie;
import java.util.Vector;
//定义主题,以及定义观察者数组,并实现增、删及通知操作。
public class Subjecct {
//观察者的存储集合,不推荐ArrayList,线程不安全,
private Vector<Observer> list = new Vector<>();
// 注册观察者方法
public void registerObserver(Observer obs) {
list.add(obs);
}
// 删除观察者方法
public void removeObserver(Observer obs) {
list.remove(obs);
}
// 通知所有的观察者更新
public void notifyAllObserver(int state) {
for (Observer observer : list) {
observer.update(state);
}
}
}1、 定义具体的,他继承继承Subject类,在这里实现具体业务,在具体项目中,该类会有很多;
package com.lijie;
//具体主题
public class RealObserver extends Subjecct {
//被观察对象的属性
private int state;
public int getState(){
return state;
}
public void setState(int state){
this.state=state;
//主题对象(目标对象)值发生改变
this.notifyAllObserver(state);
}
}1、 运行测试;
package com.lijie;
public class Client {
public static void main(String[] args) {
// 目标对象
RealObserver subject = new RealObserver();
// 创建多个观察者
ObserverImpl obs1 = new ObserverImpl();
ObserverImpl obs2 = new ObserverImpl();
ObserverImpl obs3 = new ObserverImpl();
// 注册到观察队列中
subject.registerObserver(obs1);
subject.registerObserver(obs2);
subject.registerObserver(obs3);
// 改变State状态
subject.setState(300);
System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState());
System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState());
System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState());
// 改变State状态
subject.setState(400);
System.out.println("obs1观察者的MyState状态值为:"+obs1.getMyState());
System.out.println("obs2观察者的MyState状态值为:"+obs2.getMyState());
System.out.println("obs3观察者的MyState状态值为:"+obs3.getMyState());
}
}