设计模式混编:观察者模式+中介者模式,被面试官问的Java问题难倒了

public void doSomething2() {

super.c1.selfMethod1();

super.c2.selfMethod2();

}

}

  优缺点分析
优点

中介者模式减少了类间的相互依赖,它用中介者和同事的一对多交互代替了原来同事之间的多对多交互,一对多关系更容易理解、维护和扩展,将原本难以理解的网状结构转换成相对简单的星型结构。

缺点

中介者模式内中介者会膨胀得很大, 而且包含着大量同事类之间的交互细节,原本N个对象直接的相互依赖关系转换为中介者和同事类的依赖关系,同事类越多,中介者的逻辑就越复杂。

  使用场景

中介者模式适用于多个对象之间紧密耦合的情况,紧密耦合的标准是在类图中出现了蜘蛛网状结构。中介者模式可以将蜘蛛网状结构转变为星型结构,使原本复杂混乱的关系变得清晰且简单。

事件触发器最佳实践


  事件触发器任务

今天,我们的任务是来模拟一个事件触发器。有一个产品,它有多个触发事件,它产生的时候会触发一个创建事件,在修改的时候触发修改事件,删除的时候触发删除事件,初始化的时候要触发一个onCreat事件,修改的时候触发onChange事件,双击的时候又触发onDoubleClick事件,例如Button按钮。

根据基本任务需求来看,对于多事件动作,我们可以采用工厂方法模式。此外,考虑我们的产品(如GUI设计)经常会使用复制粘贴操作,所以我们也是有一个很明显的设计模式可以使用-原型模式。结合到权限问题,也就是我们的产品并不是谁想产生就产生的,否则触发创建事件的门槛也太容易和简单了。因此,在设计中,我们的产品只能由工厂类创建,而不能被其他对象通过new方式创建,那么在这里,单来源调用(Single Call)方法可以解决。

由此我们可以看一下UML图:

实现逻辑比较简单,在此可以注意一下isCreateProduct方法和Product的构造函数为何传递进来了一个工厂对象ProductManager。没错,正是解决产品生产的权限问题。

在工厂类ProductManager中定义了一个私有变量isPermittedCreate,该变量只有在工厂类的createProduct函数中才能设置为true。在创建产品的时候,产品类Product的构造函数要求传递工厂对象,然后判断是否能够创建产品,即使你想使用类似这样的方法:

Productp=new Product(newProductManager(),“abc”);

也是不能创建出产品的。所以说在产品类中限制了两个生产条件,第一是必须是当前有效的工厂,第二就是拿到了生产资格。所以单来源调用的定义就很明显了,我们将这种一个对象只能由固定的对象初始化的方法叫做单来源调用。

//Product产生一个新的产品

public Product(ProductManager manager, String name) {

//允许建立产品

if (manager.isCreateProduct()) {

canChanged = true;

this.name = name;

}

}

产生事件的对象有了之后,我们就触发事件了。与此同时,也是要考虑事件的处理对象的。自然而然,观察者模式就可以出场了。那么,UML可以升级为如下所示:

观察者模式的设计框架已基本显现,被观察者是Product产品,观察者是EventDispatch事件分发器,具体事件处理我们后面讲,消息的传播对象是ProductEvent事件。接下来,我们看一下具体代码细节。

/**

  • @author la.lda

  • @date 2020-12-07

*/

public class Product implements Cloneable {

//产品名称

private String name;

//是否可以属性变更

private boolean canChanged = false;

//产生一个新的产品

public Product(ProductManager manager, String name) {

//允许建立产品

if (manager.isCreateProduct()) {

canChanged = true;

this.name = name;

}

}

public String getName() {

return name;

}

public void setName(String name) {

if (canChanged) {

this.name = name;

}

}

@Override

public Product clone() {

Product p = null;

try {

p = (Product) super.clone();

} catch (CloneNotSupportedException e) {

e.printStackTrace();

}

return p;

}

}

/**

  • @author la.lda

  • @date 2020-12-07

*/

public enum ProductEventType {

//新建一个产品

NEW_PRODUCT(1),

//删除一个产品

DEL_PRODUCT(2),

//修改一个产品

EDIT_PRODUCT(3),

//克隆一个产品

CLONE_PRODUCT(4);

private int value = 0;

ProductEventType(int value) {

this.value = value;

}

public int getValue() {

return this.value;

}

}

ProductEventType定义了4个事件类型,分别是新建、修改、删除以及克隆。

/**

  • @author la.lda

  • @date 2020-12-07

*/

public class ProductEvent extends Observable {

//事件起源

private Product source;

//事件的类型

private ProductEventType type;

//传入事件的源头, 默认为新建类型

public ProductEvent(Product p) {

this(p, ProductEventType.NEW_PRODUCT);

}

//事件源头以及事件类型

public ProductEvent(Product p, ProductEventType type) {

this.source = p;

this.type = type;

//事件触发

notifyEventDispatch();

}

//获得事件的始作俑者

public Product getSource() {

return source;

}

//获得事件的类型

public ProductEventType getEventType() {

return this.type;

}

//通知事件处理中心

private void notifyEventDispatch() {

super.addObserver(EventDispatch.getEventDispatch());

super.setChanged();

super.notifyObservers(source);

}

}

在产品事件对象中,增加了一个私有方法notifyEventDispatch,该方法的作用就是增加事件观察者,并在有参构造进行初始化时被调用,通知观察者。

前面说到,我们采用工厂模式对多事件进行处理,如新建、删除等。而现在产品和事件作为两个独立的对象,如何将两者进行组合关联呢?那工厂类就需要新增一个功能,组合产品和事件,产生有价值的产品事件。

ProductManager的代码如下:

/**

  • @author la.lda

  • @date 2020-12-07

*/

public class ProductManager {

//是否可以创建一个产品

private boolean isPermittedCreate = false;

//建立一个产品

public Product createProduct(String name) {

//首先修改权限,允许创建

isPermittedCreate = true;

Product product = new Product(this, name);

//产生一个创建事件

new ProductEvent(product, ProductEventType.NEW_PRODUCT);

return product;

}

//废弃一个产品

public void abandonProduct(Product product) {

//销毁一个产品,例如删除数据库记录

new ProductEvent(product, ProductEventType.DEL_PRODUCT);

product = null;

}

//修改一个产品

public void editProduct(Product product, String name) {

//修改后产品

new ProductEvent(product, ProductEventType.EDIT_PRODUCT);

product.setName(name);

}

//获得是否可以创建一个产品

public boolean isCreateProduct() {

return isPermittedCreate;

}

//克隆一个产品

public Product clone(Product product) {

//产生克隆事件

new ProductEvent(product, ProductEventType.CLONE_PRODUCT);

return product.clone();

}

}

可以看出,每个事件动作下面都增加了事件产生机制,通过组合的形式,产品和事件在扩展性上都有很强的提升。

讲述完被观察者以及广播消息后,我们来看一下下游节点-观察者。刚才说到,我们构建了一个事件分发器,为什么要有如此的设计呢?可以想象的到,对于一个事件,自然会有多个处理者,而且一个处理者处理完之后还可能通知其他的处理者。在扩展能力上,我们有新处理者加入之后是否会影响到现有的设计框架呢?因此,本文另外一个设计模式-中介模式就可以上场了。我们将EventDispatch作为中介者,事件的分发器,而事件的处理这都是具体的同事类,它们有独立的处理产品事件的逻辑。当然,我们既然有了一个中心控制,也是可以增加一个功能-权限管理,即EventDispatch能决定观察者能处理什么事件,不能处理什么事件。

因此,EventDispatch有三个职责:

  • 事件的观察者

  • 事件分发器

  • 事件处理者管理员

那么,我们现在可以完成最后一轮设计结构上的升级了。

事件分发器EventDispatch代码:

import java.util.ArrayList;

import java.util.Observable;

import java.util.Observer;

/**

  • @author la.lda

  • @date 2020-12-07

*/

public class EventDispatch implements Observer {

//单例模式

private final static EventDispatch dispatch = new EventDispatch();

//事件消费者

private ArrayList customerList = new ArrayList();

//不允许生成新的实例

private EventDispatch() {

}

//获得单例对象

public static EventDispatch getEventDispatch() {

return dispatch;

}

@Override

public void update(Observable o, Object arg) {

//事件的源头

Product product = (Product) arg;

//事件

ProductEvent event = (ProductEvent) o;

//处理者处理,这里是中介者模式的核心,可以是很复杂的业务逻辑

for (EventCustomer e : customerList) {

//处理能力是否匹配

for (EventCustomType t : e.getCustomType()) {

if (t.getValue() == event.getEventType().getValue()) {

e.exec(event);

}

}

}

}

//注册事件处理者

public void registerCustomer(EventCustomer customer) {

customerList.add(customer);

}

}

在EventDispatch里使用ArrayList存储所有的事件处理者,然后在update方法中使用了比较简单for循环完成业务逻辑的判断,其中第一层轮询事件处理者,第二层则轮询事件处理者所能处理的事件类型。只要有事件处理者的处理类型和事件类型相匹配,则调用事件处理方法exec,进入具体事件处理者的特定逻辑中。

在这里我们对事件处理者也抽象了一层,抽象类EventCustomer定义了事件处理者通用的能力,也标示出事件处理者必须具备的行为,即定义每个处理者的处理类型。当然,这里也是能够处理多个事件的。

import java.util.ArrayList;

/**

  • @author la.lda

  • @date 2020-12-07

*/

public abstract class EventCustomer {

//容纳每个消费者能够处理的级别

private ArrayList customType = new ArrayList();

//每个消费者都要声明自己处理哪一类别的事件

public EventCustomer(EventCustomType type) {

addCustomType(type);

}

//每个消费者可以消费多个事件

public void addCustomType(EventCustomType type) {

customType.add(type);

}

//得到自己的处理能力

public ArrayList getCustomType() {

return customType;

}

//每个事件都要对事件进行声明式消费

public abstract void exec(ProductEvent event);

}

/**

  • @author la.lda

  • @date 2020-12-07

*/

public enum EventCustomType {

//新建立事件

NEW(1),

//删除事件

DEL(2),

//修改事件

EDIT(3),

//克隆事件

CLONE(4);

private int value = 0;

EventCustomType(int value) {

this.value = value;

}

public int getValue() {

return value;

}

}

可以看出,EventCustomType的定义与事件类型ProductEventType基本相同。当然采用一套类型也是可以的。但从长远上来说,个人建议还是区分出来,因为无法保证观察者只有一个消息广播来源,也可能在另一组被观察者上有其他的事件类型发生。

接下来,定义三个具体的事件处理者,分别对不同事件类型进行业务逻辑处理。

/**

  • @author la.lda

  • @date 2020-12-07

*/

public class Senior extends EventCustomer {

public Senior() {

super(EventCustomType.EDIT);

super.addCustomType(EventCustomType.CLONE);

}

@Override

public void exec(ProductEvent event) {

//事件的源头

Product p = event.getSource();

//事件类型

ProductEventType type = event.getEventType();

if (type.getValue() == EventCustomType.CLONE.getValue()) {

System.out.println(“高级专家处理事件:” + p.getName() + “克隆,事件类型=” + type);

} else {

System.out.println(“高级专家处理事件:” + p.getName() + “修改,事件类型=” + type);

}

}

}

/**

  • @author la.lda

  • @date 2020-12-07

*/

public class Middle extends EventCustomer {

public Middle() {

super(EventCustomType.DEL);

}

@Override

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后总结我的面试经验

2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

BAT面试经验

实战系列:Spring全家桶+Redis等

其他相关的电子书:源码+调优

面试真题:


中…(img-50CwINPr-1711879236786)]
[外链图片转存中…(img-mp1mcAR6-1711879236786)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-ELVYDlLA-1711879236786)]

最后总结我的面试经验

2021年的金三银四一眨眼就到了,对于很多人来说是跳槽的好机会,大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。

另外,面试中遇到不会的问题不妨尝试讲讲自己的思路,因为有些问题不是考察我们的编程能力,而是逻辑思维表达能力;最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。

[外链图片转存中…(img-AM0oUDo9-1711879236787)]

BAT面试经验

实战系列:Spring全家桶+Redis等

[外链图片转存中…(img-gzsSoImU-1711879236787)]

其他相关的电子书:源码+调优

[外链图片转存中…(img-YURe7fGK-1711879236788)]

面试真题:

[外链图片转存中…(img-WT47tTni-1711879236788)]

[外链图片转存中…(img-mCi8k6h5-1711879236788)]

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值