设计模式——观察者模式
最简单的理解就是订阅发布,几个组件通过代码向控制器订阅信息,这些信息发生改变的时候,就会统一回调所有组件的回调函数并把最新数据带过来,就比如你要监听某个对象的值的变化,肯定就会把自己的函数传给被观察者对象,然后当值发生改变时就调用这一群函数,为了统一的实现这种代码思想,我们抽象出了观察者模式,统一定义了:
观察者接口(定义回调函数的函数原型)
被观察者接口(定义管理观察者的函数原型)来完成这个事情
Ps:注意观察者模式仅仅是在原有的代码上实现了多个接口,以声明观察者相关的函数原型,对原有逻辑不产生影响
观察者接口
/**
* 观察者接口
* 只要实现了这个接口,都可以加入被观察者对象的监听队列
*/
public interface Observer {
public void update(Object oldValue,Object newValue);
}
被观察者接口
/**
* 被观察者接口,需要统一一个管理观察者队列的方法,所以要这个接口
* 为了规范,当然这个方法可以不必要
*/
public interface Observerable {
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver(Object oldValue,Object newValue);
}
观察者对象举例
/**
*
* @只需要在需要通知组件中,实现Observer,就可以需要通知
*/
public class OtherCommponet implements Observer {
@Override
public void update(Object oldValue, Object newValue) {
System.out.println("旧值:"+oldValue);
System.out.println("新值:"+newValue);
}
}
/**
*
* @只需要在需要通知组件中,实现Observer,就可以需要通知
*/
public class ServiceCommponet implements Observer {
@Override
public void update(Object oldValue, Object newValue) {
System.out.println("旧值:"+oldValue);
System.out.println("新值:"+newValue);
}
}
被观察者举例
public class DataBase implements Observerable {
private String value;
public void setValue(String value){
String oldValue=this.value;
this.value=value;
/**
* 这个是观察者模式的关键
* 观察者模式设计复杂一点,还可以针对字段去做屏蔽订阅,当然这就要看读者自己设计了,反正我们目前仅仅统一一个通知方法调用
* 并无法知道是哪个字段被修改了
*/
notifyObserver(oldValue,value);
}
public DataBase(String value) {
list = new ArrayList<Observer>();
this.value=value;
}
/**
* 观察者管理相关api
*/
private List<Observer> list;
@Override
public void registerObserver(Observer o) {
list.add(o);
}
@Override
public void removeObserver(Observer o) {
if(!list.isEmpty())
list.remove(o);
}
@Override
public void notifyObserver(Object oldValue,Object newValue) {
list.forEach(observer ->{
observer.update(oldValue,newValue);
});
}
}
测试
public class _Test01 {
public static void main(String[] args) {
/**
* 随便创建一些组件,只要类实现了观察者接口就可以注册进DataBase的监听队列中
*/
ServiceCommponet serviceCommponet1=new ServiceCommponet();
ServiceCommponet serviceCommponet2=new ServiceCommponet();
OtherCommponet otherCommponet1=new OtherCommponet();
OtherCommponet otherCommponet2=new OtherCommponet();
/**
* 向DataBase监听队列注册观察者
* 一旦值发生改变就会通知这些观察者
*/
DataBase dataBase =new DataBase("你好");
dataBase.registerObserver(otherCommponet1);
dataBase.registerObserver(serviceCommponet2);
dataBase.registerObserver(otherCommponet1);
dataBase.registerObserver(otherCommponet2);
/*
因为在这里嵌入的监听代码,所以会通知所有观察者值的变化
*/
dataBase.setValue("再见");
}
}
复杂的观察者设计可以参考监听vue利用监听器实现两个对象之间的相互绑定,在java中可以考虑利用spring机制实现两个组件之间的监听关联,利用接口信息泛型实现精确到字段的监控以及整个对象的监控等等
设计模式——命令模式
命令模式主要解决的问题是,
统一对一个外部代码的调用
;比如,远程执行Fegin代理是一个命令元,而我们在业务代码中conmuser就是一个命令代理对象,里面封装命令对象,命令对象又封装了命令元;设想一下,如果我们直接写http请求,那如果想对所有调用做一个耗时统计岂不是每处都要加逻辑???所以基于统一外部代码调用问题,我们使用命令模式见面命令元进行封装并拆分成一个个命令对象组合成一个个命令客户端或宏命令给用户调用,我们需要客户端内整体做一个功能扩展,在调用上我们可以完成很多通用功能,比如:端点统计,端点判断、日志参数记录
命令元接口
public interface AudioPlayerService {
public void play();
public void rewind();
public void stop();
}
命令元实现
public class AudioPlayerServiceImpl implements AudioPlayerService {
public void play(){
System.out.println("播放...");
}
public void rewind(){
System.out.println("倒带...");
}
public void stop(){
System.out.println("停止...");
}
}
命令对象接口
public interface Command {
void execute();
}
命令对象
命令元中每个函数可能就成立一个命令对象;可以在这段代码对命令请求进行处理来决定逻辑
public class PlayCommand implements Command {
private AudioPlayerService myAudio;
public PlayCommand(AudioPlayerService audioPlayerService){
myAudio = audioPlayerService;
}
public void execute() {
myAudio.stop();
}
}
public class RewindCommand implements Command {
private AudioPlayerService myAudio;
public RewindCommand(AudioPlayerService audioPlayerService) {
myAudio = audioPlayerService;
}
public void execute() {
myAudio.rewind();
}
}
public class StopCommand implements Command {
private AudioPlayerService myAudio;
public StopCommand(AudioPlayerService audioPlayerService) {
myAudio = audioPlayerService;
}
public void execute() {
myAudio.stop();
}
}
客户端实现
用于访问代理性的访问命令对象
@Data
public class XdocCommbandClient {
private Command playCommand;
private Command rewindCommand;
private Command stopCommand;
/**
* 执行播放方法
*/
public void play(){
playCommand.execute();
}
/**
* 执行倒带方法
*/
public void rewind(){
rewindCommand.execute();
}
/**
* 执行播放方法
*/
public void stop(){
stopCommand.execute();
}
}
测试
这个用例我们可以理解为,统一访问和调用AudioPlayerService 的方式,利用XdocCommbandClient 去调用而不是直接函数对象调用
public class _Test01 {
public static void main(String[] args) {
/**
* @命令元
* 命令元对象,不同最终的实现的调用就不同,可以可以利用接口形式,动态切换命令元
*/
AudioPlayerService audioPlayerService = new AudioPlayerServiceImpl();
/**
* @命令包装对象
*(1)统一对外表现也是Command,所以从这点看,命令与功能的实际对应是解耦
*(2)对象在调用命令元前,可以对命令进行逻辑判断决定拒绝还是接受执行
*/
Command playCommand = new PlayCommand(audioPlayerService);
Command rewindCommand = new RewindCommand(audioPlayerService);
Command stopCommand = new StopCommand(audioPlayerService);
/**
* @客户端
* (1)定义命令注入功能的实际操作,比如播放可以注入倒带,倒带可以注入播放————命令函数与命令实现灵活调配
* (2)将端点功能解耦出来,比如发起命令计算时间,结束命令计算时;
* (3)针对请求结果决定此命令如何并返回结果到最外部
*/
XdocCommbandClient xdocCommbandClient = new XdocCommbandClient();
xdocCommbandClient.setPlayCommand(rewindCommand);
xdocCommbandClient.setRewindCommand(playCommand);
xdocCommbandClient.setStopCommand(stopCommand);
/**
* 外部調用方法
*/
xdocCommbandClient.play();
xdocCommbandClient.rewind();
xdocCommbandClient.stop();
}
}
宏命令
就是不是对象式的存储命令对应函数,而是用一个数组,先一个个添加然后遍历去调用
public interface MacroCommand extends Command {
/**
* 宏命令聚集的管理方法
* 可以添加一个成员命令
*/
public void add(Command cmd);
/**
* 宏命令聚集的管理方法
* 可以删除一个成员命令
*/
public void remove(Command cmd);
}
public class MacroAudioCommand implements MacroCommand {
private List<Command> commandList = new ArrayList<Command>();
/**
* 宏命令聚集管理方法
*/
@Override
public void add(Command cmd) {
commandList.add(cmd);
}
/**
* 宏命令聚集管理方法
*/
@Override
public void remove(Command cmd) {
commandList.remove(cmd);
}
/**
* 执行方法
*/
@Override
public void execute() {
for(Command cmd : commandList){
cmd.execute();
}
}
}
public class _Test02 {
public static void main(String[] args) {
/**
* @宏命令的意义
* 就是不是对象式的存储命令对应函数,而是用一个数组,先一个个添加然后遍历去调用
*/
AudioPlayerService audioPlayerService = new AudioPlayerServiceImpl();
Command playCommand = new PlayCommand(audioPlayerService);
Command rewindCommand = new RewindCommand(audioPlayerService);
Command stopCommand = new StopCommand(audioPlayerService);
MacroCommand marco = new MacroAudioCommand();
marco.add(playCommand);
marco.add(rewindCommand);
marco.add(stopCommand);
marco.execute();
}
}
命令模式中,命令元和命令对象都是搭配性写死的,修改的情况只能是替换元对象或者命令对象的拒绝和接受机制
但是命令客户端、宏命令是可以随意组合任意命令对象
设计模式——责任链模式
通常每个接收者都包含对另一个接收者的引用。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推
主要解决:职责链上的处理者负责处理请求,客户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。。
何时使用:在处理消息的时候以过滤很多道。
如何使用:不引入原有代码统一继承是抽象类,抽象类实现了对下一个职责人的对象的保存以及调用
职责链责任抽象管理类
主要利用链表的形式实现链式对接
public abstract class AbstractLogger {
/**
* @利用链表形式指向下一个原生
*/
protected AbstractLogger nextLogger;
public void setNextLogger(AbstractLogger nextLogger) {
this.nextLogger = nextLogger;
}
public AbstractLogger getNextLogger() {
return this.nextLogger;
}
/**
* 用户自己定义的
*
* @param message
*/
abstract protected void handle(String message);
/**
* 对外部的统一调用,用户自定义的方法
*
* @param message
*/
public void logMessage(String message) {
handle(message);
if (nextLogger != null) {
nextLogger.logMessage(message);
}
}
}
职责链处理实现类
主要就是提供模板方法说明当前责任对象的处理方法即可
public class ConsoleLogger extends AbstractLogger {
public ConsoleLogger(){
}
@Override
protected void handle(String message) {
System.out.println("Standard Console::Logger: " + message);
}
}
public class ErrorLogger extends AbstractLogger {
public ErrorLogger(){
}
@Override
protected void handle(String message) {
System.out.println("Error Console::Logger: " + message);
}
}
public class FileLogger extends AbstractLogger {
public FileLogger(){
}
@Override
protected void handle(String message) {
System.out.println("File::Logger: " + message);
}
}
测试
public class _Test01 {
private static AbstractLogger getChainOfLoggers(){
/**
* 创建被配置链表关系
* 注意,在真实环境中一般都是利用链表管理区统一配置的而不是这样直接手动去set
*/
AbstractLogger errorLogger = new ErrorLogger();
AbstractLogger fileLogger = new FileLogger();
AbstractLogger consoleLogger = new ConsoleLogger();
errorLogger.setNextLogger(fileLogger);
fileLogger.setNextLogger(consoleLogger);
/**
* 返回链表头部
*/
return errorLogger;
}
public static void main(String[] args) {
AbstractLogger loggerChain = getChainOfLoggers();
/**
* 调用统一的外部方法,然后递归一个个调下一个链条
*/
loggerChain.logMessage( "This is an information.");
}
}
只需要记住一点,责任链就是链表
设计模式——适配器模式
适配器模式主要解决问题就是,方法调用方式不一样,所以为了统一格式,我们需要统一方法的接口,在原有的函数封装成接口指定函数内,对外可以多态表现为适配器对象;
适配器统一了某个组件所接受的其他组件的所有的原型,比如方法参数可以以适配器作为类型,想与这个组件通讯的组件就必须实现适配器
根据适配的形式不同,我们可以以下两种适配器;
外包置入元对象型:
需要针对每个实现类写适配Impl做一个转化,
public class _Test02 {
static interface XdocPlayerAdpater {
void start();
}
static class XdocPlayerAdpaterImpl implements XdocPlayerAdpater{
private AlibabaMediaPlayer alibabaMediaPlayer;
public XdocPlayerAdpaterImpl(AlibabaMediaPlayer alibabaMediaPlayer){
this.alibabaMediaPlayer=alibabaMediaPlayer;
}
public void start() {
alibabaMediaPlayer.play();
}
}
static class AlibabaMediaPlayer{
public void play(){
System.out.println("播放音乐");
}
}
public static void main(String[] args) {
AlibabaMediaPlayer alibabaMediaPlayer=new AlibabaMediaPlayer();
XdocPlayerAdpater xdocPlayerAdpater=new XdocPlayerAdpaterImpl(alibabaMediaPlayer);
xdocPlayerAdpater.start();
}
}
接口直接实现型:
直接在原有接口实现适配即可,但可能侵入原有代码
public class _Test01 {
static interface XdocPlayerAdpater {
void start();
}
static class AlibabaMediaPlayer implements XdocPlayerAdpater {
public void play(){
System.out.println("播放音乐");
}
public void start() {
play();
}
}
public static void main(String[] args) {
XdocPlayerAdpater alibabaMediaPlayer=new AlibabaMediaPlayer();
alibabaMediaPlayer.start();
}
}
设计模式——享元模式
字符串是典型的享元模式的应用,为了减少对象的创建占用内存,但可能增加远程访问的负担;实现方式也非常简单,利用哈希匹配创建过的则就不必在创建了;
public class StringFactory {
private Map<String,String> stringPool=new ConcurrentHashMap<>();
public String createString(String str){
String metaString = stringPool.get(str);
if(metaString!=null){
return metaString;
}
return new String(str);
}
}