代码写的烂,经常被同事怼,教你一招!
面对复杂的业务场景,千变万化的客户需求,如何以一变应万变,以最小的开发成本快速落地实现,同时保证系统有着较低的复杂度,能够保证系统后续de持续迭代能力,让系统拥有较高的可扩展性。
本文是主要讲解桥接模式
、组合模式
、装饰模式
、门面模式
、代理模式
、责任链模式
将抽象部分与它的实现部分分离,使它们都可以独立地变化。
一个类存在两个(或多个)独立变化的维度,且这两个(或多个)维度都需要独立进行扩展。 对于那些不希望使用继承或因为多层继承导致系统类的个数急剧增加的系统,桥接模式尤为适用。
抽象实体:定义的一种抽象分类。比如:人 具体实体:继承抽象实体的子类实体。比如:中国人、美国人、韩国人 抽象行为:定义抽象实体中具备的多种行为。比如:学汉语、吃汉堡 具体行为:实现抽象行为的具体算法。比如:中国人学汉语、美国人吃汉堡
/**
* @author 微信公众号:微观技术
* 抽象实体
*/
public abstract class AbstractEntity {
protected AbstractBehavior abstractBehavior;
public AbstractEntity(AbstractBehavior abstractBehavior) {
this.abstractBehavior = abstractBehavior;
}
public abstract void out();
}
/**
* 抽象行为
*/
public interface AbstractBehavior {
public String action(String name);
}
/**
* 关于食物的行为
*/
public class FoodBehavior implements AbstractBehavior {
@Override
public String action(String name) {
if ("中国人".equals(name)) {
return "吃 饺子";
} else if ("美国人".equals(name)) {
return "吃 汉堡";
}
return null;
}
}
本质是将一个对象的实体和行为分离,然后再基于这两个维度进行独立的演化。
拆分复杂的类对象时。当一个类中包含大量对象和方法时,既不方便阅读,也不方便修改。 希望从多个独立维度上扩展时。比如,系统功能性和非功能性角度,业务或技术角度等。 运行时,组合不同的组件
组合模式也称整体模式,把一组相似的对象当作一个单一的对象,然后将对象组合成树形结构以表示整个层次结构。
抽象组件(AbstractNode):定义需要实现的统一操作。 组合节点(CompositeNode):抽象组件的衍生子类,包含了若干孩子节点(其它组合节点或叶子节点)。 叶子节点(LeafNode):抽象组件的子类,但它的下面没有子节点。
public abstract class AbstractNode {
public abstract void add(AbstractNode abstractNode);
public abstract void remove(AbstractNode abstractNode);
public abstract void action();
}
public class CompositeNode extends AbstractNode {
private Long nodeId;
private List<AbstractNode> childNodes; //存放子节点列表
public CompositeNode(Long nodeId, List<AbstractNode> childNodes) {
this.nodeId = nodeId;
this.childNodes = childNodes;
}
@Override
public void add(AbstractNode abstractNode) {
childNodes.add(abstractNode);
}
@Override
public void remove(AbstractNode abstractNode) {
childNodes.remove(abstractNode);
}
@Override
public void action() {
for (AbstractNode childNode : childNodes) {
childNode.action();
}
}
}
public class LeafNode extends AbstractNode {
private Long nodeId;
public LeafNode(Long nodeId) {
this.nodeId = nodeId;
}
@Override
public void add(AbstractNode abstractNode) {
// 无子节点,无需处理
return;
}
@Override
public void remove(AbstractNode abstractNode) {
// 无子节点,无需处理
return;
}
@Override
public void action() {
System.out.println("叶子节点编号:" + nodeId);
}
}
树形结构
、环形结构
、网状结构
。如我们常见的 深度优先搜索
、广度优先搜索
都是采用这种模式。一组对象按照某种层级结构进行管理。如:管理文件夹和文件,管理订单下的商品。 需要按照统一的行为来处理复杂结构中的对象 快速扩展对象组合。
动态地向一个现有对象添加新的职责和行为,同时又不改变其结构,相当于对现有的对象进行包装。
抽象组件(Component):装饰器基类,定义组件的基本功能 具体组件(ConcreteComponent):抽象组件的具体实现 抽象装饰器(Decorator):包含抽象组件的引用 具体装饰器(ConcreteDecorator):抽象装饰器的子类,并重写组件接口方法,同时可以添加附加功能。
public abstract class Component {
public abstract void execute();
}
public class ConcreteComponent extends Component {
@Override
public void execute() {
System.out.println("具体子类 ConcreteComponent invoke !");
}
}
public class Decorator extends Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
@Override
public void execute() {
component.execute();
}
}
public class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
@Override
public void execute() {
System.out.println("装饰器子类 ConcreteDecorator invoke !");
super.execute();
}
}
无需修改代码的情况下即可使用对象, 且希望在运行时为对象新增额外的功能 将业务逻辑组织为层次结构,可以为各层创建一个装饰,在运行时将各种不同逻辑组合成对象。由于这些对象都遵循通用接口,客户端代码能以相同的方式使用这些对象。 不支持继承扩展类的场景。如: final
关键字限制了某个类的进一步扩展,可以通过装饰器对其进行封装,从而具备扩展能力。
门面模式提供一个高层次的接口,要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,使得子系统更易于使用。
slf4j
框架,其内部统一了log4j
、log4j2
、CommonLog
等日志框架,简化了我们的开发成本。门面系统。接收外部请求,并将请求转发给适当的子系统进行处理 子系统。表示某个领域内的功能实现、或者具体子接口实现,比如,订单、支付等,专门处理由门面系统指派的任务。
简化复杂系统,提供统一接口规范。比如: JPA
提供了统一Java持久层API,底层适配多样化的存储系统。复杂的业务逻辑由内部子系统消化,只要对外接口规范不变,外部调用方不需要频繁修改 扩展性较好,类似于 SPI架构
一样,支持水平扩展。较高的平滑过渡性。比如:我们要对老的系统架构升级,开发一系列新接口来替换原来的老接口,过渡期需要新老灰度测试、流量切换、平滑升级,可以采用该模式。门面模式在兼容多套系统、系统重构方面是把利器。
为其他对象提供一种代理以控制对这个对象的访问
房产中介 包工头
抽象主题类(AbstractSubject):定义接口方法,供客户端使用 主题实现类(RealSubject):实现了 抽象主题类
的接口方法代理类(Proxy):实现了 抽象主题类
的接口方法,内部包含主题实现类
的逻辑, 同时还包含一些自身的扩展操作。
代理模式与适配器模式相似。但适配器模式是转换为新的接口,而代理模式不会改变原有接口。
/**
* @author 微信公众号:微观技术
*/
public interface AbstractSubject {
void execute();
}
public class RealSubject implements AbstractSubject {
@Override
public void execute() {
System.out.println("我是Tom哥,我要努力工作!");
}
}
public class Proxy implements AbstractSubject {
private AbstractSubject abstractSubject;
public Proxy(AbstractSubject abstractSubject) {
this.abstractSubject = abstractSubject;
}
@Override
public void execute() {
System.out.println("老板给Tom哥分配工作了。。。");
abstractSubject.execute();
}
}
静态代理,代理类需要自己编写代码完成。 动态代理,代理类通过 Proxy#newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法生成。 JDK实现的代理中不管是静态代理还是动态代理,都是面向接口编程。CGLib可以不限制一定是接口。
职责清晰 高扩展,只要实现了接口,都可以用代理 智能化,动态代理 降低了对象的直接耦合
远程代理。无法直接操作远程对象。比如:Dubbo、gRPC,提供远程服务,客户端调用时需要走参数组装、序列化、网络传输等操作,这些通用逻辑都可以封装到代理中,客户端调用代理对象访问远程服务,就像调用本地对象一样方便。 保护代理。当客户端通过代理对象访问原始对象时,代理对象会根据规则判断客户端是否有权限访问。比如:防火墙 日志代理。比如:日志监控,正常业务访问时,调用代理,增加一些额外的日志记录功能。 虚拟代理,适用于延迟初始化,用小对象表示大对象的场景,减少资源损耗,提升运行速度。 不希望改变原对象,但需要增加类似于权限控制、日志、流控等附加功能时,可以使用代理模式。
责任链模式是一种行为设计模式,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链。收到请求后,每个处理者均可对请求进行处理,或将其传递给链中的下个处理者。
链表
结构的具体应用。抽象处理者(Handler):定义一个接口,内部包含处理方法和下一个节点的引用对象 具体处理者(ConcreteHandler): 抽象处理者
的实现子类,判断本次请求是否处理,如果需要则处理,否则跳过,然后将请求转发给下一个节点。
降低了对象之间的耦合度。链上各个节点各司其职,通过上下文传递数据,避免直接依赖。 增强系统的可扩展性。如果有新的业务需求,只需要在合适的位置增加一个链节点即可,满足开闭原则。 灵活性强。如果业务有变化,需要对工作流程做调整,只需要动态调整链上节点的次序即可。甚至为了满足多元化业务的多样化需求,我们可以为不同的业务类型定义自己的专属执行顺序。 简化了对象之间的连接。每个对象只需保存下一个节点的引用,而不需保持所有节点。 责任明确。每个节点只需处理自己的工作,如果不处理则传递给下一个对象。明确各类的责任范围,符合类的单一职责原则。
在设计中思考什么应该变化,并封装会发生变化的概念。
SOLID
原则,自我review是否满足业务的持续扩展性。有句话说的好,“不论白猫黑猫,能抓老鼠就是好猫。”有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️