设计模式是在软件工程领域中用于解决常见问题的重复利用的经验性解决方案。它们是一种经过验证的、被广泛接受的设计方法,可以帮助开发人员构建更健壮、可维护、可扩展和可重用的软件系统。设计模式提供了一种在特定情境下解决问题的方法,有助于开发人员更容易地理解和沟通
设计模式的主要目的是解决软件设计中的常见问题,包括对象创建、结构组织、行为控制等。它们提供了可重用的、经验丰富的解决方案,有助于降低错误发生的可能性。

源码位置

设计模式原则

设计模式的六大原则

1、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。

 

设计模式类别


​​​抽象文档(Abstract Document)
抽象工厂(Abstract Factory)
主动对象(Active Object)
无环访问者(Acyclic Visitor)
适配器(Adapter)
聚合微服务(Aggregator Microservices)
大使(Ambassador)
API 网关(API Gateway)
安排/执行/断言(Arrange/Act/Assert)
异步方法调用(Async Method Invocation)
阻塞(Balking)
桥接(Bridge)
建造者(Builder)
业务代理(Business Delegate)
字节码(Bytecode)
缓存(Caching)
回调(Callback)
责任链(Chain of responsibility)
断路器(Circuit Breaker)
客户端会话模式(Client Session Pattern)
收集参数(Collecting Parameter)
集合管道(Collection Pipeline)
组合器(Combinator)
命令(Command)
指挥官(Commander)
组件(Component)
复合(Composite)
复合实体(Composite Entity)
复合视图(Composite View)
上下文对象(Context object)
转换器(Converter)
命令查询职责分离(CQRS)
奇怪的递归模板模式(Curiously Recurring Template Pattern)
柯里化(Currying)
数据访问对象(Data Access Object)
数据总线(Data Bus)
数据局部性(Data Locality)
数据映射器(Data Mapper)
数据传输对象(Data Transfer Object)
装饰者(Decorator)
委托(Delegation)
依赖注入(Dependency Injection)
脏标志(Dirty Flag)
领域模型(Domain Model)
双缓冲(Double Buffer)
双重检查锁定(Double Checked Locking)
双重分发(Double Dispatch)
嵌入值(Embedded Value)
事件聚合器(Event Aggregator)
基于事件的异步(Event-based Asynchronous)
事件驱动架构(Event Driven Architecture)
事件队列(Event Queue)
事件溯源(Event Sourcing)
环绕执行(Execute Around)
扩展对象(Extension objects)
外观(Facade)
工厂(Factory)
工厂工具包(Factory Kit)
工厂方法(Factory Method)
扇出/扇入(Fan-Out/Fan-In)
特性开关(Feature Toggle)
过滤器(Filterer)
流畅接口(Fluent Interface)
Flux
享元(Flyweight)
前端控制器(Front Controller)
游戏循环(Game Loop)
保护悬挂(Guarded Suspension)
半同步/半异步(Half-Sync/Half-Async)
六边形架构(Hexagonal Architecture)
标识映射(Identity Map)
拦截过滤器(Intercepting Filter)
解释器(Interpreter)
迭代器(Iterator)
层次(Layers)
惰性加载(Lazy Loading)
领导者选举(Leader Election)
领袖/跟随者(Leader/Followers)
可锁定对象(Lockable Object)
标记接口(Marker Interface)
主从(Master-Worker)
中介者(Mediator)
备忘录(Memento)
元数据映射(Metadata Mapping)
模型-视图-控制器(Model-View-Controller)
模型-视图-意图(Model-View-Intent)
模型-视图-呈现器(Model-View-Presenter)
模型-视图-视图模型(Model-View-ViewModel)
模块(Module)
单子(Monad)
监视器(Monitor)
单态(MonoState)
多例(Multiton)
哑巴模式(Mute Idiom)
裸对象(Naked Objects)
空对象(Null Object)
对象生成器(Object Mother)
对象池(Object Pool)
观察者(Observer)
乐观离线锁(Optimistic Offline Lock)
页面控制器(Page Controller)
页面对象(Page Object)
参数对象(Parameter Object)
部分响应(Partial Response)
管道(Pipeline)
毒丸(Poison Pill)
表现模型(Presentation Model)
优先队列模式(Priority Queue Pattern)
私有类数据(Private Class Data)
生产者-消费者(Producer Consumer)
承诺(Promise)
属性(Property)
原型(Prototype)
代理(Proxy)
基于队列的负载平衡(Queue based load leveling)
反应器(Reactor)
读写锁(Reader Writer Lock)
注册表(Registry)
存储库(Repository)
资源获取即初始化(Resource Acquisition Is Initialization)
重试(Retry)
角色对象(Role Object)
Saga
分离接口(Separated Interface)
序列化实体模式(Serialized Entity Pattern)
仆人(Servant)
服务层(Service Layer)
服务定位器(Service Locator)
服务到工作器(Service to Worker)
分片(Sharding)
单例(Singleton)
空间分区(Spatial Partition)
特例(Special Case)
规范(Specification)
状态(State)
步骤构建器(Step Builder)
藤蔓模式(Strangler)
策略(Strategy)
子类沙盒(Subclass Sandbox)
表模块(Table Module)
模板方法(Template method)
线程本地存储(Thread-local storage)
线程池(Thread Pool)
节流(Throttling)
宽容读者(Tolerant Reader)
蹦床(Trampoline)
事务脚本(Transaction Script)
双胞胎(Twin)
类型对象(Type-Object)
工作单元(Unit Of Work)
更新方法(Update Method)
值对象(Value Object)
版本号(Version Number)
访问者(Visitor)

结构型模式

这些模式关注对象之间的组合和关系,旨在解决如何构建灵活且可复用的类和对象结构。

设计模式-适配器模式(Adapter)

定义

使一个类和接口不匹配的其他类进行交互。 适配器可帮助两个不兼容的接口一起工作。 接口可能不兼容,但内部功能应适合需要。 适配器设计模式允许不兼容的类通过转换来一起工作
将一类的接口转换为客户端期望的接口。

意图

  • 将一个类的接口转换为client类期望的另一个接口
  • 适配器让类可以协同工作,否则会因为接口不兼容无法进行。

种类

  • 类适配器  通过继承实现:从不兼容的类派生出新类并且添加我们需要的方法使得派生类满足预期接口。
  • 对象适配器 通过对象组合实现:新类中包含原始类并且在新类里创建方法以实现调用的转换。

参与者

  • Target : 定义client使用的特定于某个域的接口。
  •  Adapter: 将Adaptee接口适配到Target接口。
  • Adaptee: 定义需被适配的已有接口
  • Client : 与遵循Target接口的对象协作。

举例(source:https://github.com/iluwatar/java-design-patterns

故事是这样的:  海盗来了! 我们需要一个划艇{RowingBoat}逃离! 我们只有一个渔船 {FishingBoat},队长。 我们没有时间在造一个新船!

我们需要重用这个渔船。 船长需要一艘可以操作的划艇。 然后使用适配器模式让船长可以使用渔船。

该例子使用对象适配器 ,也就是将渔船类通过组合的方式包含在适配器中

类图:

Client :Captain 

public class Captain {

  private RowingBoat rowingBoat;

  public Captain() {}

  public Captain(RowingBoat rowingBoat) {
    this.rowingBoat = rowingBoat;
  }

  public void setRowingBoat(RowingBoat rowingBoat) {
    this.rowingBoat = rowingBoat;
  }

  public void row() {
    rowingBoat.row();
  }

}

Adaptee:FishingBoat

public class FishingBoat {

  private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);

  public void sail() {
    LOGGER.info("The fishing boat is sailing");
  }

}

Adapter: FishingBoatAdapter

public class FishingBoatAdapter implements RowingBoat {

  private FishingBoat boat;

  public FishingBoatAdapter() {
    boat = new FishingBoat();
  }

  @Override
  public void row() {
    boat.sail();
  }
}

Target:RowingBoat


public interface RowingBoat {

  void row();

}

给这个船长一个渔船让他开走

  public static void main(String[] args) {
    // The captain can only operate rowing boats but with adapter he is able to use fishing boats as well
    Captain captain = new Captain(new FishingBoatAdapter());
    captain.row();
  }

 

优点

  • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
  • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
  • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

类适配器模式还具有如下优点:

由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。

对象适配器模式还具有如下优点:

一个对象适配器可以把多个不同的适配者适配到同一个目标,也就是说,同一个适配器可以把适配者类和它的子类都适配到目标接口。

缺点

类适配器模式的缺点如下:

对于Java、C#等不支持多重继承的语言,一次最多只能适配一个适配者类,而且目标抽象类只能为抽象类,不能为具体类,其使用有一定的局限性,不能将一个适配者类和它的子类都适配到目标接口。

对象适配器模式的缺点如下:

与类适配器模式相比,要想置换适配者类的方法就不容易。如果一定要置换掉适配者类的一个或多个方法,就只好先做一个适配者类的子类,将适配者类的方法置换掉,然后再把适配者类的子类当做真正的适配者进行适配,实现过程较为复杂。

桥接设计模式(Bridge Pattern)

定义

桥接模式(Bridge Pattern)是一种结构型设计模式,它用于将抽象部分与实现部分分离,以便它们可以独立地变化。桥接模式的核心思想是分离抽象和实现,使它们可以独立演化。这个模式将系统的抽象部分和实现部分分为两个独立的继承层次结构,从而降低了它们之间的耦合性。

意图

  • 分离抽象与实现:桥接模式的核心目标是分离抽象部分(抽象类或接口)与实现部分(具体实现类)以降低二者之间的耦合。这使得抽象和实现可以独立变化,不会相互影响。

  • 独立的变化:桥接模式允许抽象部分和实现部分独立演化。当需要引入新的抽象部分或实现部分时,不必修改已有的代码,这提高了系统的灵活性和可维护性。

  • 组合优于继承:桥接模式强调"组合优于继承"的设计原则。它避免了类层次结构的爆炸,通过将多个维度的变化分离,将类之间的关系简化为关联关系。

  • 多态性:桥接模式允许在运行时动态地选择不同的实现,从而实现多态性。这有助于实现不同组合下的多样性行为。

  • 增加灵活性和可扩展性:桥接模式增加了系统的灵活性,允许更容易地适应变化。当需求发生变化时,可以轻松地添加新的抽象或实现。

参与者

  • 抽象部分(Abstraction):定义了抽象部分的接口,通常包含一些高级操作,它内部包含对实现部分的引用。抽象部分可以是一个抽象类或接口。

  • 扩展抽象部分(Refined Abstraction):继承自抽象部分,扩展了抽象部分的接口,并可以包含一些具体的业务逻辑。

  • 实现部分(Implementor):定义实现部分的接口,通常也是一个抽象类或接口。

  • 具体实现部分(Concrete Implementor):实现实现部分的接口,提供具体的功能。

举例

某公司为庆周年庆,向客户发送一批优惠券,优惠券有不同的类型和金额,那么显然创建所有类型所有金额的优惠券显然是不现实的,这里使用桥接模式

类图:

java bridge design pattern

这个例子中对应类的角色如下:

  • 抽象化(Abstraction)Coupon 接口就是抽象化角色,它定义了基于 DiscountType 的 applyCoupon 方法。

  • 扩充抽象化(Refined Abstraction)FixedAmountCoupon 和 PercentageCoupon 类就是扩充抽象化角色,它们是 Coupon 接口的具体实现。

  • 实现化(Implementor)DiscountType 接口就是实现化角色,它定义了 applyDiscount 方法。

  • 具体实现化(Concrete Implementor)AmountDiscountFixedDiscount 和 TieredDiscount 类就是具体实现化角色,它们是 DiscountType 接口的具体实现。

在这个例子中,Coupon 和 DiscountType 之间的桥接使得 Coupon 可以独立于 DiscountType 变化,也就是说,你可以自由地添加新的 Coupon 类型和 DiscountType 类型,而不需要修改现有的代码。

 

Coupon

public interface Coupon {
    void applyCoupon(double amount);
}

 

固定数量优惠券

public class FixedAmountCoupon implements Coupon {
    private DiscountType discountType;
    private double fixedAmount;

    public FixedAmountCoupon(DiscountType discountType, double fixedAmount) {
        this.discountType = discountType;
        this.fixedAmount = fixedAmount;
    }

    @Override
    public void applyCoupon(double amount) {
        discountType.applyDiscount(amount - fixedAmount);
    }
}

 

折扣优惠券

public class PercentageCoupon implements Coupon {
    private DiscountType discountType;
    private double percentage;

    public PercentageCoupon(DiscountType discountType, double percentage) {
        this.discountType = discountType;
        this.percentage = percentage;
    }

    @Override
    public void applyCoupon(double amount) {
        discountType.applyDiscount(amount * (1 - percentage));
    }
}

 

折扣类型

public interface DiscountType {
    void applyDiscount(double amount);
}

 

不同的折扣实现类

@Slf4j
// 固定金额的优惠额
public class AmountDiscount implements DiscountType {
    @Override
    public void applyDiscount(double amount) {
        log.info("应付金额: " + amount);
    }
}


@Slf4j
//根据购物金额的折扣
public class TieredDiscount implements DiscountType {
    @Override
    public void applyDiscount(double amount) {
        double discount;
        if (amount < 100) {
            discount = 0.05;
        } else if (amount < 200) {
            discount = 0.1;
        } else {
            discount = 0.15;
        }
        log.info("应付金额: " + (amount * (1 - discount)));
    }
}

 

测试

@Slf4j
public abstract class App implements DiscountType {
    public static void main(String[] args) {
        DiscountType discountType = new AmountDiscount();
        DiscountType tieredType = new TieredDiscount();
        Coupon fixedAmountCoupon = new FixedAmountCoupon(discountType, 10);
        Coupon percentageCoupon = new PercentageCoupon(discountType, 0.25);
        Coupon tieredPercentageCoupon = new PercentageCoupon(tieredType, 0.1);

        log.info("固定金额优惠券:");
        fixedAmountCoupon.applyCoupon(100);

        log.info("百分比优惠券:");
        percentageCoupon.applyCoupon(100);

        //折上折
        log.info("根据金额计算折扣优惠券:");
        tieredPercentageCoupon.applyCoupon(100);
    }
}

 

运行结果

 

优点

  1. 分离抽象与实现:桥接模式将抽象部分与实现部分分离,使它们可以独立变化。这有助于降低类之间的耦合,使系统更加灵活,允许它们独立演化。

  2. 可扩展性:由于桥接模式允许添加新的抽象部分和新的实现部分,它提供了很好的扩展性。您可以轻松地引入新的维度,而不需要修改现有的代码。

  3. 多态性:桥接模式允许在运行时动态地选择实现,从而实现多态性。这有助于实现不同组合下的多样性行为。

  4. 避免类爆炸问题:在某些情况下,如果不使用桥接模式,可能需要大量的子类,导致类的爆炸问题。桥接模式可以减少子类的数量,使代码更可管理。

  5. 分离接口和实现:桥接模式有助于分离接口和实现,这样接口不会受到实现的影响,实现也不会受到接口的影响。这使得修改接口或实现变得更加容易。

  6. 复用性:由于桥接模式的分离性,可以更容易地重用抽象部分和实现部分,减少代码冗余。

  7. 适应性:桥接模式使系统更容易适应变化。当需求发生变化时,您可以轻松地添加新的抽象或实现,而不会破坏现有的结构。

缺点

  1. 复杂性增加:引入桥接模式会增加代码的复杂性,因为它需要创建多个抽象和具体类,这可能会增加项目的代码量。

  2. 抽象和实现的分离:虽然抽象和实现的分离有助于灵活性,但也可能导致代码的分散和可读性降低。程序员需要明确了解抽象和实现之间的关系,以避免混淆。

  3. 设计复杂性:当只有一个维度的变化时,使用桥接模式可能会显得过于复杂。在这种情况下,桥接模式可能会引入不必要的复杂性,增加代码维护成本。

  4. 性能开销:在某些情况下,桥接模式可能引入性能开销,因为需要通过抽象部分调用实现部分,可能导致额外的方法调用和开销。

  5. 适用性:桥接模式通常更适用于那些有多个维度需要独立变化的情况。如果系统只有一个维度的变化,引入桥接模式可能是过度工程。

 

Spring JdbcTemplate中桥接模式的使用

Slf4j日志中桥接模式的应用

组合设计模式(Composite)

定义

组合模式(Composite Pattern)是一种结构性设计模式,它允许你将对象组合成树形结构,以表示"部分-整体"层次结构。这个模式允许客户端统一地对待单个对象和组合对象。

意图

  • 将对象组织成树形结构:组合模式允许将对象组织成层次结构,其中单个对象和组合对象都被视为相同类型的组件。

  • 客户端统一访问:客户端可以统一地访问单个对象和组合对象,不需要区分它们的具体类型,因为它们都实现了相同的接口。

  • 简化客户端代码:组合模式简化了客户端代码,因为客户端不需要处理对象的类型,而只需调用通用的接口方法。

  • 支持递归结构:组合模式支持递归结构,因此可以方便地处理深层次的组合对象。

  • 增加新类型的组件:组合模式使得增加新类型的组件变得相对容易,因为只需要实现通用的接口。

参与者(角色)

  • 组件(Component):这是一个抽象类或接口,它定义了组合对象和单个对象的通用接口。所有具体组件类都必须实现这个接口,它包括方法如显示名称、添加子组件、移除子组件等。

  • 叶子(Leaf):这是组合模式中的单个对象,它实现了组件接口。叶子对象没有子组件,通常表示了树结构中的最小单元。

  • 组合(Composite):这是组合模式中的组合对象,它实现了组件接口。组合对象可以包含其他组件,包括叶子对象和其他组合对象,形成一个树形结构。

举例

以下是某公司的组织机构实现的组合模式

类图:

这个例子中对应类的角色如下:

  • Component(组件):抽象的接口或类,定义了单个对象和组合对象的通用方法。在示例中,Component 接口定义了 showName() 方法,表示显示对象的名称。

  • Company(公司):具体的组合对象,实现了 Component 接口。Company 类表示公司,可以包含其他组件,包括子公司和部门。它负责管理子组件的添加和删除。

  • SubsidiaryCompany(子公司):具体的组合对象,也实现了 Component 接口。SubsidiaryCompany 类表示子公司,可以包含其他组件,包括部门。它同样负责管理子组件的添加和删除。

  • Department(部门):叶子对象,实现了 Component 接口。Department 类表示部门,它是组织结构的最小单元,没有子组件。

组织抽象接口

public interface Organization {
    void showName();
}

 

公司类

@Slf4j
public class Company implements Organization{
    private String name;
    private List<Organization> children = new ArrayList<>();

    public Company(String name) {
        this.name = name;
    }

    public void add(Organization component) {
        children.add(component);
    }

    public void remove(Organization component) {
        children.remove(component);
    }

    @Override
    public void showName() {
        log.info("公司名字: " + name);
        for (Organization component : children) {
            component.showName();
        }
    }
}  }
}

 

子公司

@Slf4j
public class SubsidiaryCompany implements Organization{
    private String name;
    private List<Organization> children = new ArrayList<>();

    public SubsidiaryCompany(String name) {
        this.name = name;
    }

    public void add(Organization component) {
        children.add(component);
    }

    public void remove(Organization component) {
        children.remove(component);
    }

    @Override
    public void showName() {
        log.info("子公司: " + name);
        for (Organization component : children) {
            component.showName();
        }
    }
}

 

部门

@Slf4j
public class Department implements Organization{
    private String name;

    public Department(String name) {
        this.name = name;
    }

    @Override
    public void showName() {
        log.info("部门名称: " + name);
    }
}

 

测试,组合一个组织机构

public class App {
    public static void main(String[] args) {
        Company parentCompany = new Company("科技公司");

        SubsidiaryCompany subsidiary1 = new SubsidiaryCompany("北京分公司");
        Department hrDepartment = new Department("北京Hr部门");
        Department devDepartment = new Department("北京研发部门");
        subsidiary1.add(hrDepartment);
        subsidiary1.add(devDepartment);

        SubsidiaryCompany subsidiary2 = new SubsidiaryCompany("广州分公司");
        Department finaceDepartment = new Department("广州财务部门");
        Department saleDepartment = new Department("广州销售部门");
        subsidiary2.add(finaceDepartment);
        subsidiary2.add(saleDepartment);

        parentCompany.add(subsidiary1);
        parentCompany.add(subsidiary2);

        parentCompany.showName();
    }
}

 

优点

  1. 统一接口:组合模式允许客户端统一地对待单个对象和组合对象,因为它们都实现了相同的接口,从而简化了客户端代码。

  2. 灵活性:组合模式支持递归结构,可以方便地处理深层次的组合对象。你可以轻松地构建具有多层次结构的对象。

  3. 可扩展性:向组合结构中添加新类型的组件相对容易,因为只需要实现通用的接口即可,不需要修改现有代码。

  4. 可维护性:组合模式使代码更加清晰,因为它减少了与特定对象类型的耦合,使代码更易于维护和扩展。

缺点

  1. 复杂性:组合模式可能会引入额外的复杂性,因为它创建了一个对象层次结构。这可能会使代码难以理解和调试。

  2. 性能开销:递归遍历组合结构可能会导致性能开销,特别是当结构非常深或包含大量组件时。

  3. 限制操作:组合模式通常不支持在组合对象中添加或删除子对象的操作,因为这些操作可能会破坏整体结构。

  4. 过度一般化:在某些情况下,组合模式可能会过于一般化,不适用于所有类型的对象组织结构。

组合模式的应用

Hashmap中的组合模式

Map接口对应Component

HashMap对应Composite

Node对应叶子

 

装饰器设计模式

定义

装饰器模式是一种结构型设计模式,它允许你在不改变对象自身结构的情况下,动态地添加功能或责任给对象。这种模式通过创建一个装饰器类,用于包装原始类的实例,在装饰器中可以添加新的行为或修改现有行为,同时保持接口的一致性。

这个模式的关键点在于装饰器类和被装饰的类实现同一个接口或继承自同一个父类,从而使得装饰器可以透明地替代被装饰的对象。通过嵌套多个装饰器,可以灵活地组合各种功能,实现对对象行为的动态增加或修改。

意图

  • 动态地给对象添加功能: 通过装饰器,可以在运行时动态地给对象添加新的行为或修改现有行为,而不是在编译时固定功能。这种动态性让你能够在不修改现有代码的情况下扩展对象的功能。

  • 保持接口的一致性: 装饰器模式的关键是装饰器和被装饰的对象实现相同的接口或继承自相同的父类,这样客户端代码可以无缝地使用原始对象或装饰后的对象,而不需要关心具体的装饰器实现。

  • 单一职责原则: 装饰器模式支持单一职责原则,每个装饰器类都只关注于单一的功能,使得代码更加模块化和可维护。

参与者(角色)

  • Component(组件): 定义被装饰的对象的接口,可以是抽象类或接口,它是装饰器模式中被装饰的对象的基础类型。

  • ConcreteComponent(具体组件): 实现了 Component 接口的具体对象,是被装饰的原始对象,它可以添加新的功能。

  • Decorator(装饰器抽象类): 继承自 Component,保持对 Component 的引用,并定义一个与 Component 接口一致的接口。它可以是抽象类或者接口,其子类可以通过添加新的行为来装饰 Component 对象。

  • ConcreteDecorator(具体装饰器): 继承自 Decorator,实现具体的装饰功能。它包含一个指向 Component 的引用,并可以调用父类的方法以及添加新的行为,以扩展 Component 对象的功能。

举例

以某人每天穿什么衣服,去哪儿为例

类图:

这个例子中对应类的角色如下:

  • Component(组件):Human

  • ConcreteComponent(具体组件): Person

  • Decorator(装饰器抽象类): Decorator

  • ConcreteDecorator(具体装饰器):Decorator_first,Decorator_second
     

Human 接口

public interface Human {
	    public void wearClothes();
        public void walkToWhere();
}

 

Person

public class People implements Human{
	  @Override  
	    public void wearClothes() {  
	        // TODO Auto-generated method stub  
	        System.out.println("穿什么呢。。");  
	    }  
	  
	    @Override  
	    public void walkToWhere() {  
	        // TODO Auto-generated method stub  
	        System.out.println("去哪里呢。。");  
	    }  
}

 

Decorator

public abstract class Decorator implements Human {
        Human human;
        public Decorator(Human human){
        	this.human =human;
        }
		@Override
		public void wearClothes() {
		      human.wearClothes();
		}
		@Override
		public void walkToWhere() {
			human.walkToWhere();
		}
        
}

 

Decorator_first

public class Decorator_first extends Decorator {

	/**
	 * @param human
	 */
	public Decorator_first(Human human) {
		super(human);
	}
	
	public void goClothespress() {  
        System.out.println("去衣柜找找看。。");  
    }  
  
    public void findPlaceOnMap() {  
        System.out.println("在Map上找找。。");  
    }

	@Override
	public void wearClothes() {
		super.wearClothes();
		goClothespress() ;
	}

	@Override
	public void walkToWhere() {
		// TODO Auto-generated method stub
		super.walkToWhere();
		findPlaceOnMap();
	}  
  

}

 

Decorator_second

public  class Decorator_second extends Decorator {

	/**
	 * @param human
	 */
	public Decorator_second(Human human) {
		super(human);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void wearClothes() {
		// TODO Auto-generated method stub
		super.wearClothes();
		findClothes();
	}

	@Override
	public void walkToWhere() {
		// TODO Auto-generated method stub
		super.walkToWhere();
		findDestination();
	}
	
	public void findClothes(){
		System.out.println("找到一件T血");
	}
	public void findDestination(){
		System.out.println("找到一家电影院");
	}
	
}

 

测试

	public static void main(String[] args) {
		Human human  = new People();
		Decorator decorator = new Decorator_second(new Decorator_first(human));
		decorator.wearClothes();
		decorator.walkToWhere();
	}

 

优点

  1. 灵活性和扩展性: 装饰器模式允许动态地给对象添加新的功能,而不需要改变其结构。通过嵌套装饰器,可以灵活地组合多个功能,实现更复杂的行为,使得系统更容易扩展和维护。

  2. 避免类爆炸: 装饰器模式避免了通过大量子类来扩展对象功能所带来的类爆炸问题。它通过组合的方式,允许你在运行时选择不同的装饰器来动态地添加功能,而不是静态地通过继承来扩展对象。

  3. 单一职责原则: 每个装饰器类都只关注于单一的功能,使得代码更加模块化和可维护。这有助于遵循单一职责原则,每个类都有清晰明确的责任。

  4. 保持对象接口的一致性: 装饰器模式的关键在于装饰器和被装饰的对象实现相同的接口或继承自相同的父类,这样客户端代码可以无缝地使用原始对象或装饰后的对象,而不需要关心具体的装饰器实现。

  5. 易于移除功能: 由于装饰器可以动态添加和移除功能,所以在不需要某些功能时可以很容易地移除它们,而不影响其他部分代码。

缺点

  1. 复杂性增加: 在使用装饰器模式时,可能会引入许多小型对象,这些对象分别负责不同的功能。这样一来,可能会增加代码的复杂性,使得理解和维护代码变得困难。

  2. 多层装饰可能导致混乱: 当多个装饰器嵌套使用时,可能会导致代码结构变得混乱。理解对象是如何被装饰以及装饰器之间的交互可能变得复杂。

  3. 潜在的性能影响: 大量嵌套的装饰器可能会引入额外的性能开销。每个装饰器都需要执行一些逻辑以处理其责任,这可能会导致运行时的性能损失,尤其是在复杂的装饰器链中。

  4. 可能导致过度设计: 如果过度使用装饰器模式,可能会导致设计变得过于灵活和复杂,超出了实际需求。这可能会增加开发和维护的成本,而且并不是所有情况都需要这种高度的灵活性。

 

装饰器模式的应用

java中Collections.synchronizedList() 方法是一种装饰器的应用,它可以将线程不安全的list包装成为一个线程安全的list,而不需要修改原有的ArrayList 。它的实现即对原有list的方法是用关键字synchronized 同步达到安全访问的目的。

 

shardingsphere中对合并结果的装饰

 

责任链模式(Chain of responsibility)

定义

责任链模式旨在解耦发出请求的对象和处理请求的对象。它包括一系列处理对象和一个起始点。当一个请求进入这条链时,它沿着链传递,直到有一个对象能够处理该请求,或者请求到达链的末端。

意图

  • 请求者和处理者的解耦: 将发送者与接收者解耦,使得发送者不需要知道请求的具体处理者是谁,以及它们是如何处理请求的。

  • 动态创建对象链: 允许在运行时动态地组织对象链和配置处理对象,增加、删除或重新排列处理对象,从而灵活地修改请求的处理方式。

  • 分发责任: 每个处理对象只负责自己能够处理的请求,将请求发送给适当的处理者,实现责任的分担和职责的分离。

  • 可扩展性和可维护性: 由于责任链模式中的对象链是动态创建的,因此可以方便地扩展和维护。新增处理者或调整处理顺序不需要修改客户端代码。

  • 避免请求的无法处理: 确保每个请求都有一个处理者能够处理,如果当前处理者无法处理请求,可以将其传递给下一个处理者,直至请求被处理。

参与者(角色)

  • Handler(处理者): 定义处理请求的接口,并且通常包含一个指向下一个处理者的引用。该接口通常包含一个处理请求的方法。

  • ConcreteHandler(具体处理者): 实现了处理请求的接口。如果自己能够处理请求,则处理它;否则,将请求传递给链中的下一个处理者。

  • Client(客户端): 创建并发送请求给责任链的起始点。客户端并不需要知道请求是由哪个具体的处理者来处理的。

举例

以员工请假,领导审批为例

类图:

这个例子中对应类的角色如下:

  • Handler(处理者):Leader

  • ConcreteHandler(具体处理者): Manager,GroupLeader,Boss

  • Client(客户端): LeaveRequest
     

Leader类,有名字和下一个领导属性

public abstract class Leader {
	 protected  String name;
	 protected Leader nextLeader;
	 public Leader getNextLeader() {
		return nextLeader;
	}
	public void setNextLeader(Leader nextLeader) {
		this.nextLeader = nextLeader;
	}
	public Leader(String name){
		 this.name =name;
	 }
	public abstract  void handleRequest(LeaveRequest    request);
		
}

 

Manager 

public class Manager extends Leader {

	/**
	 * @param name
	 */
	public Manager(String name) {
		super(name);
	}


	@Override
	public void handleRequest(LeaveRequest request) {
		String name = request.getName();
		String reason = request.getReason();
		int days = request.getDays();
		if (days <= 15) {
			System.out.println(name + "需要请假" + days + "天,请假理由" + reason
					+ "经理同意审批!");
		} else {
			System.out.println("超过15天的假期需要董事长审批!");
			if (this.nextLeader != null) {
				this.nextLeader.handleRequest(request);
			}
		}
	}

}

 

Boss 具体处理者

public class Boss extends Leader {

	/**
	 * @param name
	 */
	public Boss(String name) {
		super(name);
	}
	
	@Override
	public void handleRequest(LeaveRequest request) {
		String name = request.getName();
		String reason = request.getReason();
		int days = request.getDays();
		if (days <= 30) {

			System.out.println(name + "需要请假" + days + "天,请假理由" + reason
					+ " 董事长同意审批!");
		} else {
			System.out.println("我无法处理该请假天数,需要部门商量");
			if (this.nextLeader != null) {
				this.nextLeader.handleRequest(request);
			}else{
				System.out.println("暂时不能请假");
			}
		}
	}

}

 

LeaveRequest,包含请假的参数

public class LeaveRequest {
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getDays() {
		return days;
	}
	public void setDays(int days) {
		this.days = days;
	}
	public String getReason() {
		return reason;
	}
	public void setReason(String reason) {
		this.reason = reason;
	}
	private String name;
	private int days;
	private String reason;
	
}

 

测试

	public static void main(String[] args) {
			Leader groupLeader = new GroupLeader("部门组长");
			Leader  manager = new Manager("部门经理");
			Leader  boss = new Boss("董事长");
			groupLeader.setNextLeader(manager);
			manager.setNextLeader(boss);
			LeaveRequest leaveRequest = new LeaveRequest();
			leaveRequest.setDays(31);
			leaveRequest.setName("张三");
			leaveRequest.setReason("肚子疼");
			groupLeader.handleRequest(leaveRequest);
	}

//output

超过5天的假期需要经理审批
超过15天的假期需要董事长审批!
我无法处理该请假天数,需要部门商量
暂时不能请假

 

优点

  1. 解耦责任者和请求者: 责任链模式使得请求者和处理者之间解耦,请求者不需要知道具体的处理者,而处理者也不需要知道请求的发送者,降低了对象之间的耦合度。

  2. 灵活性和可扩展性: 可以动态地增加或修改处理者,改变处理者之间的连接顺序,或者改变处理者的职责,从而灵活地调整和扩展处理流程。

  3. 单一职责原则: 每个具体处理者都只关注自己负责处理的请求类型,符合单一职责原则,易于维护和修改。

  4. 降低耦合度: 因为每个处理者只需知道自己的后继者,处理者之间的关系松散,易于独立开发、测试和维护。

  5. 可以控制处理顺序: 可以通过调整处理者之间的连接顺序来控制请求的处理顺序,可以按照需求定义处理者的优先级。

缺点

  1. 可能导致请求未被处理: 如果责任链中没有合适的处理者来处理请求,或者所有处理者都没有正确配置,请求可能会到达链的末端而无法被处理。

  2. 性能问题: 如果责任链过长或者处理者选择不当,可能会导致请求在链中传递过程中出现性能问题。

  3. 难以调试: 当责任链比较长时,可能会增加代码的复杂性和难以调试的难度。

 

责任链模式的应用

Netty ChannelPipeline 

通过addLast方法构成一个handler处理链

public class ServerInitializer extends ChannelInitializer<SocketChannel> {


    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        System.out.println(pipeline.toString());
        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new FileUploadHandler());
    }
}
   public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);

            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventLoop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }

    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

 

 

代理设计模式(Proxy)

定义

代理模式是一种结构型设计模式,该模式通过创建一个代理类,代理类可以替代原始对象,并在客户端和原始对象之间充当中间层。这种方式可以在不改变原始对象代码的情况下,对其进行间接访问和控制。代理对象可以在执行实际请求前后进行一些附加操作,例如记录日志、权限校验、延迟加载等。

意图

  • 为另一个对象提供代理或占位符以控制对其的访问。

参与者(角色)

  • Subject(抽象主题):定义了代理类和真实主题的公共接口,这样代理类可以通过实现该接口来代理真实主题。

  • RealSubject(真实主题):实际执行业务逻辑的类,是代理所代表的对象。代理对象通过调用真实主题来完成实际的工作。

  • Proxy(代理类):实现了与抽象主题相同的接口,包含对真实主题的引用。代理类可以在完成其自身职责之前或之后,对真实主题的方法进行控制或增强。

举例

以通过vpn访问资源为例

类图:

这个例子中对应类的角色如下:

  • Subject(抽象主题):NetworkResource

  • RealSubject(真实主题):RealNetworkResource

  • Proxy(代理类):ResourceVPNProxy

NetworkResource

// 定义一个接口表示资源
interface NetworkResource {
    void fetchResource();
}

 

RealNetworkResource类

// 实际的网络资源类
@Slf4j
class RealNetworkResource implements NetworkResource {
    private final String url;

    RealNetworkResource(String url) {
        this.url = url;
    }

    @Override
    public void fetchResource() {
        log.info("Fetching resource from URL: " + url);
    }
}

 

ResourceVPNProxy

@Slf4j
public class ResourceVPNProxy implements NetworkResource {
    private final String url;
    private RealNetworkResource realNetworkResource;

    ResourceVPNProxy(String url) {
        this.url = url;
    }

    @Override
    public void fetchResource() {
        if (realNetworkResource == null) {
            realNetworkResource = new RealNetworkResource(url);
        }
        // 可以在这里添加额外的逻辑,代理服务器获取获取资源
        log.info("代理服务即将开始获:{}上的资源",url);
        realNetworkResource.fetchResource();
        log.info("获取到资源");
    }
}

 

测试

public class App {
    public static void main(String[] args) {
        // 创建一个代理对象,访问特定的 URL
        NetworkResource proxy = new ResourceVPNProxy("https://example.com");
        // 通过代理访问资源
        proxy.fetchResource();
    }
}

 

优点

  1. 远程代理(Remote Proxy):允许本地对象访问远程对象,使得远程对象可以像本地对象一样被访问。

  2. 虚拟代理(Virtual Proxy):用于控制对创建开销较大的对象的访问,只有在需要时才会真正创建对象。

  3. 安全代理(Protection Proxy):控制对对象的访问权限,保护真实主题免受未经授权的访问。

  4. 缓存代理(Cache Proxy):为开销大的操作提供临时存储,以提高性能。

  5. 简化客户端:隐藏了真实主题的复杂性,使客户端无需了解实际的实现细节。

缺点

  1. 代理类引入复杂性:引入了额外的代理类,增加了系统复杂性。

  2. 可能引起性能问题:在某些情况下,代理模式可能会引入一定的性能开销,特别是在代理类与真实主题的通信开销较大或者频繁访问时。

  3. 需要对每个方法进行代理:如果需要代理的方法很多,可能需要为每个方法都创建代理,增加了工作量。

  4. 滥用可能导致系统复杂性:过度使用代理模式可能会导致系统过度复杂化,应该根据实际需求和场景慎重选择是否使用。

代理模式的应用

SpringAop

spring在初始化后同过包装bean,然后创建代理对象,代理对象分为jdk动态代理和cglib

 

 

享元模式(Flyweight Pattern)

定义

享元模式是一种结构型设计模式,它通过共享尽可能多的对象来最小化内存使用。它适用于大量拥有相似/重复状态的对象。

意图

  • 减少内存使用:通过共享相似对象的部分状态或整个对象,减少内存占用。
  • 提高性能:通过减少相似对象的数量,降低创建和销毁对象的成本,提高系统性能。

参与者(角色)

  • 享元(Flyweight):代表了需要共享的对象,它包含了共享状态的部分,并提供了接收和使用外部状态的方法。
  • 工厂(Factory):负责创建和管理享元对象,可能包含缓存已创建的享元对象的池子。

举例

Hashtable 存储共享对象

类图:

这个例子中对应类的角色如下:

  • 享元(Flyweight)Flyweight
  • 工厂(Factory)FlyweightFactory

抽象Flyweight对象

public abstract class Flyweight {
	public abstract void flyweight();
}

 

具体享元ConcreteFlyweight对象

public class ConcreteFlyweight extends Flyweight {
	private String s;

	public ConcreteFlyweight(String str) {
		this.s = str;
	}

	@Override
	public void flyweight() {
		System.out.println("ConcreteFlyweight:" + s);
	}

}

 

工厂类FlyweightFactory

public class FlyweightFactory {
	private Hashtable<Object,Flyweight> flyweights = new Hashtable<Object,Flyweight>();

	public FlyweightFactory() {
	}

	public Flyweight getFlyWeight(Object obj) {
		Flyweight flyweight = flyweights.get(obj);
		if (flyweight == null) {
			flyweight = new ConcreteFlyweight((String) obj);
			flyweights.put(obj, flyweight);
		}
		return flyweight;
	}

	public int getFlyweightSize() {
		return flyweights.size();
	}
}

 

 享元模式FlyweightPattern

package com.zlennon.flyweight;

public class FlyweightPattern {
	FlyweightFactory factory = new FlyweightFactory();
	Flyweight fly1;
	Flyweight fly2;
	Flyweight fly3;
	Flyweight fly4;
	Flyweight fly5;
	Flyweight fly6;

	/** */
	/** Creates a new instance of FlyweightPattern */
	public FlyweightPattern() {
		fly1 = factory.getFlyWeight("Google");
		fly2 = factory.getFlyWeight("Qutr");
		fly3 = factory.getFlyWeight("Google");
		fly4 = factory.getFlyWeight("Google");
		fly5 = factory.getFlyWeight("Google");
		fly6 = factory.getFlyWeight("Google");
	}

	public void showFlyweight() {
		fly1.flyweight();
		fly2.flyweight();
		fly3.flyweight();
		fly4.flyweight();
		fly5.flyweight();
		fly6.flyweight();
		int objSize = factory.getFlyweightSize();
		System.out.println("objSize = " + objSize);
	}

}

 

测试

public class App {
    public static void main(String[] args) {
        System.out.println("The FlyWeight Pattern!");
        FlyweightPattern fp = new FlyweightPattern();
        fp.showFlyweight();
    }
}

例子中总共使用6个对象,实际上只创建了2个对象

 

 

优点

  1. 减少内存使用:共享相似状态的对象,节省内存空间。
  2. 提高性能:减少了对象的数量,提高了系统的性能。

缺点

  1. 需要区分内部状态和外部状态:如果对象有区分内部状态和外部状态,需要正确管理它们的关系,可能增加复杂性。
  2. 共享对象可能导致线程安全问题:需要考虑多线程环境下的共享对象访问问题。

代理模式的应用

Spring中bean的获取 ,如果在singletonObjects中存在则直接返回,没有则创建

Java 中的 String Pool(字符串池)

Java中的字符串池(String Pool)是Java堆中的一个特殊存储区域,用于存储字符串字面量。它的实现旨在提高字符串操作的性能并节省内存。

当从String类创建一个新的字符串字面量时,JVM会首先检查字符串是否已存在于字符串池中。如果存在,JVM会返回对现有字符串对象的引用,而不是创建一个新对象。这就是字符串池的字符串合并机制。

 

外观模式(Facade Pattern)

定义

外观模式(Facade Pattern)是一种结构型设计模式,其主要目的在于提供一个简化复杂系统接口的高级接口。它为子系统或一组接口提供了一个单一的入口点。

意图

  • 为子系统中一组接口提供统一的接口。Facade定义了一个更高级别的接口,使得子系统更容易使用。

参与者(角色)

  • 外观(Facade): 提供了一个简化的接口,客户端通过这个接口与系统交互。
  • 子系统(Subsystems): 实现了系统的各种功能,由外观统一管理和调用。

当你需要使用Facade模式的情况包括:

  1. 当你想为复杂的子系统提供一个简单的接口。随着子系统的演化,它们往往会变得更加复杂。大多数模式在应用时会导致更多、更小的类。这使得子系统更具可重用性和易于定制,但对于不需要定制的客户端来说使用起来更加困难。Facade可以提供一个简单的默认视图,对于大多数客户端来说已经足够了。只有需要更多定制的客户端才需要深入了解Facade之外的内容。
  2. 存在很多客户端和抽象化实现类之间的依赖关系。引入Facade以将子系统与客户端和其他子系统解耦,从而提高子系统的独立性和可移植性。
  3. 你希望对子系统进行分层。使用Facade定义每个子系统层级的入口点。如果子系统之间存在依赖关系,可以通过仅通过它们的Facades相互通信来简化它们之间的依赖关系。
 

举例

以淘宝购物为例,用户只需要选择商品并支付,内部处理逻辑对于客户端是隐藏的

类图:

这个例子中对应类的角色如下:

  • 外观(Facade): TaobaoFacade
  • 子系统(Subsystems): OrderSevice,PaymentService,CourierService

TaobaoFacade外观类

@Slf4j
public class TaobaoFacade {
  private PaymentService paymentService;
  private OrderSevice orderSevice;
  private CourierService courierService;

  public TaobaoFacade() {
    paymentService = new PaymentService();
    orderSevice = new OrderSevice();
    courierService = new CourierService();
  }

  public void order(String productName,int amount) {
    paymentService.pay(productName,amount);
    orderSevice.orderPlaced(productName);
    courierService.sendProduct(productName);
  }
}

 

子系统OrderSevice

@Slf4j
public class OrderSevice {
  public void orderPlaced(String productName) {
    log.info("您购买的商品{},订单已创建",productName);
  }
}

 

子系统PaymentService

@Slf4j
public class PaymentService {
  public void pay(String productName, int amount) {
    log.info("您的商品:{}已经支付,支付金额:{} ",productName,amount);
  }
}

 

 子系统CourierService

@Slf4j
public class CourierService {
  public void sendProduct(String productName) {
    log.info("您购买的{}商品运送中。。。",productName);
  }
}

 

测试

public class App {
  
  public static void main(String[] args) {
    TaobaoFacade taobaoFacade = new TaobaoFacade();
    taobaoFacade.order("iPhone15",5000);
  }
}

 

优点

  1. 简化接口: 将子系统的一系列复杂操作封装在一个简单的接口中,使客户端更容易使用。
  2. 解耦: Facade模式可以帮助客户端与子系统之间实现解耦,避免客户端直接与子系统中的多个类交互,降低了客户端与子系统之间的耦合度。
  3. 易于维护: 由于Facade模式隐藏了子系统的复杂性,因此修改子系统时只需要修改Facade而不是多个客户端。
  4. 隐藏细节: 可以隐藏系统的复杂性,使得用户无需了解系统的内部工作原理。

缺点

  1. 不灵活: 如果系统需求频繁变化,可能需要修改Facade,这可能导致Facade变得庞大且难以维护。
  2. 过度抽象: 如果Facade设计不好,可能导致过度抽象,使得客户端不能灵活地调用底层子系统的某些特性或功能。

外观模式的应用

tomcat RequestFacade

RequestFacade 将复杂的 ServletRequest 接口进行了封装,并提供了更简单、更易用的接口。

apache.commons.beanutils.Converter
ConverterFacade 提供了一个convert 方法方便客户端调用

 

 

 

过滤器模式(Filter)

定义

过滤器设计模式(Filter Pattern)是一种结构型设计模式,允许你使用不同的标准(过滤器)过滤一组对象,通过逻辑组合这些标准,可以在不改变原始对象的情况下动态地将它们连接起来。

意图

引入一个功能接口,该接口将为类似容器的对象添加功能,以便轻松返回其自身的过滤版本。

参与者(角色)

  • Subject(抽象主题):定义了代理类和真实主题的公共接口,这样代理类可以通过实现该接口来代理真实主题。

  • RealSubject(真实主题):实际执行业务逻辑的类,是代理所代表的对象。代理对象通过调用真实主题来完成实际的工作。

  • Proxy(代理类):实现了与抽象主题相同的接口,包含对真实主题的引用。代理类可以在完成其自身职责之前或之后,对真实主题的方法进行控制或增强。

举例

以过滤person集合为例

类图:

定义接口Filter

@FunctionalInterface
public interface Filter {

    List<Person> meetCriteria(List<Person> person);
}

 

Person类

class Person {
    private String name;
    private String gender;
    private String maritalStatus;

    public Person(String name, String gender, String maritalStatus) {
        this.name = name;
        this.gender = gender;
        this.maritalStatus = maritalStatus;
    }

    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public String getMaritalStatus() {
        return maritalStatus;
    }


    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", maritalStatus='" + maritalStatus + '\'' +
                '}';
    }
}

 

测试

@Slf4j
public class App {
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>();
        persons.add(new Person("John", "Male", "Single"));
        persons.add(new Person("Jane", "Female", "Married"));
        persons.add(new Person("Bob", "Male", "Married"));
        persons.add(new Person("Alice", "Female", "Single"));

        Filter maleFilter =person -> persons.stream().filter(p->p.getGender().equals("Male")).collect(Collectors.toList());
        Filter singleFilter =person -> persons.stream().filter(p->p.getMaritalStatus().equals("Single")).collect(Collectors.toList());
        Filter marriedFemaleFilter =person -> persons.stream().filter(p->p.getMaritalStatus().equals("Married")&&p.getGender().equals("Female")).collect(Collectors.toList());

        log.info("男性成员: {}" , "---------------------------------------------");
        maleFilter.meetCriteria(persons).forEach(person -> log.info(person.toString()));
        log.info("单身成员: {}" , "---------------------------------------------");
        singleFilter.meetCriteria(persons).forEach(person -> log.info(person.toString()));
        log.info("已婚女性成员: {}" , "---------------------------------------------");
        marriedFemaleFilter.meetCriteria(persons).forEach(person -> log.info(person.toString()));

    }
}

 

优点

  1. 灵活性: 过滤器模式提供了灵活性,允许根据需要组合不同的过滤器,以创建不同的过滤条件。这使得系统更容易扩展和维护。

  2. 可重用性: 每个过滤器都是相对独立的,可以在不同的上下文中重复使用。这种可重用性可以降低代码的重复性,提高代码的可维护性。

  3. 解耦: 过滤器模式可以将过滤的逻辑从主业务逻辑中解耦出来。这样,过滤器的变化不会对主业务逻辑产生影响,提高了系统的灵活性和可维护性。

缺点

  1. 性能: 当有大量的过滤器时,可能会影响系统的性能。每个过滤器都需要执行,可能导致额外的开销。在处理大规模数据时需要注意性能问题。

  2. 复杂性: 过多的过滤器可能导致系统变得复杂,降低了代码的可读性。设计过多复杂的过滤器条件可能会使系统难以理解和维护。

  3. 顺序依赖: 过滤器的执行顺序可能对结果产生影响。在某些情况下,过滤器的顺序可能需要谨慎选择,否则可能得不到期望的结果。

过滤器模式的应用

 

创建型模式

这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象。这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。

设计模式-工厂模式(Factory)

定义

工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种统一的接口来创建对象,但将具体对象的创建延迟到子类或具体工厂类中。工厂模式通过封装对象的创建过程,使客户端代码与具体对象的实例化过程分离,从而实现了松耦合和更好的代码可维护性。

意图

  • 松耦合(Loose Coupling): 工厂模式通过将对象的创建过程封装在工厂中,降低了客户端代码与具体对象的耦合程度。客户端不需要知道如何实例化具体对象,只需要与工厂接口交互,这使得代码更加灵活和易于维护。

  • 可扩展性(Scalability): 工厂模式使得在不修改现有客户端代码的情况下可以添加新的产品或具体工厂类。这样,系统可以轻松地支持新的产品类型或变种。

  • 代码复用: 工厂模式促使客户端代码重用,因为客户端通过抽象工厂接口与产品交互,可以在不修改客户端的情况下更换具体工厂类,以创建不同类型的产品。

  • 封装对象创建过程: 工厂模式将对象创建的复杂性和细节封装在具体工厂中,使客户端代码更简洁和清晰。

  • 适用于多种产品族: 抽象工厂模式引入了一层抽象工厂,可以创建一组相关的产品对象。这对于创建多种不同产品族的对象非常有用。

种类

  • 简单工厂 : 使用一个工厂对象用来生产同一等级结构中的任意产品。(不支持拓展增加产品)
  • 工厂方法 : 使用多个工厂对象用来生产同一等级结构中对应的固定产品。(支持拓展增加产品)
  • 抽象工厂 : 使用多个工厂对象用来生产不同产品族的全部产品。(不支持拓展增加产品;支持增加产品族)

参与者

  • 抽象工厂(Abstract Factory): 定义了创建产品对象的接口,通常包含一个或多个抽象的工厂方法。这是工厂模式的核心,客户端将通过这个接口来请求产品的创建。

  • 具体工厂(Concrete Factory): 实现了抽象工厂接口,负责实际创建具体产品对象。每个具体工厂通常对应创建一类产品。

  • 抽象产品(Abstract Product): 定义了产品对象的通用接口,通常包括产品对象的属性和方法的声明。

  • 具体产品(Concrete Product): 实现了抽象产品接口,表示被创建的具体对象。

  • 客户端(Client): 负责使用工厂来创建产品对象,而不需要直接实例化具体产品。客户端通过抽象工厂接口与产品进行交互,从而实现了松耦合。

举例

简单工厂

生产宝马汽车的工厂,这个工厂会生产520和525类型的宝马车

工厂的createBMW方法用于生产不同的宝马车

public  class FactoryBMW {
		public BWM createBMW(String name){
			if(name.equals("BWM520LI")){
				return new BWM520Li(name);
			}if(name.equals("BWM525LI")){
				return new BWM525Li(name);
			}else{
				return null;
			}
		};
		public static void main(String[] args) {
			FactoryBMW fb =new FactoryBMW();
			BWM  bwm525 =fb.createBMW("BWM525LI");
			BWM  bwm520 = fb.createBMW("BWM520LI");
			System.out.println(bwm520.getName());
			System.out.println(bwm525.getName());
		}
}
优点:
  1. 简单性: 简单工厂模式是一种简单的模式,易于理解和实现。它将对象的创建逻辑封装在一个工厂类中,客户端只需通过工厂类来创建对象,而不需要关心对象的具体创建细节。

  2. 封装: 简单工厂模式有助于将对象创建的复杂性封装起来,使客户端代码保持简洁和清晰。客户端不需要了解对象的创建方式或构造函数。

  3. 松耦合: 简单工厂模式降低了客户端与具体对象的耦合。客户端只需与工厂类交互,而不需要直接与具体对象类交互,这使得客户端代码更容易维护和扩展。

  4. 中心化控制: 简单工厂模式允许将对象的创建逻辑集中在一个位置,有助于统一管理和维护对象的创建过程。

缺点:
  1. 不适用于复杂对象结构: 简单工厂模式适用于创建较简单的对象,当对象的创建过程较为复杂且多样化时,简单工厂模式可能变得笨拙和难以维护。

  2. 违反开放封闭原则: 当需要添加新的产品类型时,通常需要修改工厂类的代码,这违反了开放封闭原则。这意味着维护现有的工厂类可能需要对代码进行修改,可能会导致不稳定的代码。

  3. 难以扩展工厂: 如果需要在简单工厂模式中添加新的产品类型,通常需要修改工厂类,这可能导致工厂类变得庞大和难以维护。这也可能影响其他客户端代码。


工厂方法

不同的型号使用不同的工厂创建

public class FactoryBWM520LI implements FactoryBWM {

	@Override
	public BWM createBWM() {
		
		return new BWM520Li("BWM520LI");
	}

}
public class FactoryBWM525LI implements FactoryBWM {


	@Override
	public BWM createBWM() {
		// TODO Auto-generated method stub
		return new BWM525Li("BWM525LI");
	}

}
	public static void main(String[] args) {
		FactoryBWM520LI bwm520li =new FactoryBWM520LI();
		System.out.println(bwm520li.createBWM().getName());
		FactoryBWM525LI bwm525li = new FactoryBWM525LI();
		System.out.println(bwm525li.createBWM().getName());
	}
优点:
  1. 松耦合(Loose Coupling): 工厂方法模式将具体产品的创建延迟到具体工厂类中,客户端与具体产品之间的耦合度更低,客户端只需与抽象工厂接口交互,无需直接依赖于具体产品类。

  2. 可扩展性: 工厂方法模式支持添加新的产品类型,只需创建新的具体工厂类和具体产品类,而不需要修改现有代码。这遵循开放封闭原则,使系统更易于扩展。

  3. 隐藏细节: 工厂方法模式将对象的创建过程封装在工厂类中,客户端代码无需关心对象的具体创建细节,这提高了代码的清晰度和可维护性。

  4. 多态性: 工厂方法模式允许客户端以统一的方式创建产品,从而支持多态性。客户端可以根据需要选择不同的具体工厂来创建对象,实现了多态性的特性。

  5. 适应不同需求: 工厂方法模式适用于各种不同的需求和情况,可以创建多种不同类型的产品。

缺点:
  1. 类爆炸: 如果有多个产品等级结构和多个具体工厂类,可能会导致类的数量急剧增加,这被称为类爆炸问题。管理大量的工厂和产品类可能会变得复杂。

  2. 增加复杂度: 工厂方法模式引入了更多的接口和抽象类,使系统的结构变得更加复杂。这可能增加一些开发和维护的成本。

  3. 不适用于简单情况: 对于一些简单的对象创建需求,工厂方法模式可能过于繁琐,不如简单工厂模式来得直接。


抽象工厂

520和525工厂可以别生产对应的 发动机和空调

BWM520LIFactory 

public class BWM520LIFactory implements BWMFactory {


	@Override
	public Engine createEngine() {
		// TODO Auto-generated method stub
		return new BWM520LIEngine();
	}


	@Override
	public AriCondition createAriCondition() {
		return new AirBWM520LI();
	}

}

BWM525LIFactory  

public class BWM525LIFactory implements BWMFactory {


	@Override
	public Engine createEngine() {
		// TODO Auto-generated method stub
		return new BWM525LIEngine();
	}


	@Override
	public AriCondition createAriCondition() {
		// TODO Auto-generated method stub
		return new AirBWM525LI();
	}

}
优点:
  1. 松耦合: 抽象工厂模式通过将对象的创建逻辑封装在抽象工厂中,降低了客户端代码与具体对象的耦合。客户端只需与抽象工厂接口交互,无需直接依赖于具体产品类。

  2. 可扩展性: 抽象工厂模式支持添加新的产品族,只需创建新的具体工厂类和具体产品类,而不需要修改现有代码。这遵循开放封闭原则,使系统更易于扩展。

  3. 隐藏细节: 抽象工厂模式将对象的创建过程封装在抽象工厂类中,客户端代码无需关心对象的具体创建细节,这提高了代码的清晰度和可维护性。

  4. 多态性: 抽象工厂模式允许客户端以统一的方式创建产品,从而支持多态性。客户端可以根据需要选择不同的具体工厂来创建对象,实现了多态性的特性。

  5. 一致性: 抽象工厂模式确保了一组相关的产品对象能够协同工作,因为它们由同一工厂创建,这有助于确保产品之间的一致性。

缺点:
  1. 类爆炸: 如果有多个产品族和多个具体工厂类,可能会导致类的数量急剧增加,这被称为类爆炸问题。管理大量的工厂和产品类可能会变得复杂。

  2. 增加复杂度: 抽象工厂模式引入了更多的接口和抽象类,使系统的结构变得更加复杂。这可能增加一些开发和维护的成本。

  3. 不适用于简单情况: 对于一些简单的对象创建需求,抽象工厂模式可能过于繁琐,不如简单工厂模式或工厂方法模式来得直接。

Spring中的工厂模式

BeanFactory的继承关系

Spring BeanFactory

 

BeanFactory 接口在 Spring Framework 中通常用作工厂方法模式的实现。它定义了一组方法,用于创建和管理 Bean 实例。每个具体的 Bean 工厂(如 DefaultListableBeanFactory)都实现了这个接口,负责创建特定类型的 Bean 实例。因此,BeanFactory 接口是工厂方法模式的实现,它将对象的创建委托给具体的工厂类,每个工厂类负责创建特定类型的对象(Bean)。这种模式使得代码更具弹性和可维护性,因为可以根据需要添加或更改工厂类来创建不同类型的 Bean。因此BeanFactory 接口更符合工厂方法模式的概念,它定义了一组方法用于创建和管理 Bean 实例

单例模式(Singleton)

定义

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点以确保所有代码都在使用相同的实例。单例模式的目的是限制类的实例化次数,通常用于确保系统中某个类的全局唯一性。

意图

  • 确保唯一实例: 保证一个类仅有一个实例,并提供一个全局访问点。
  • 全局访问点: 允许全局访问类的唯一实例,方便在程序的各个地方使用同一个对象。

举例

饿汉式

/**
 * 提前初始化静态实例保证了线程安全。
 */
public final class IvoryTower {

  /**
   * 私有构造器
   */
  private IvoryTower() {}

  /**
   * 实例化,类加载的时候会被初始化并赋值
   */
  private static final IvoryTower INSTANCE = new IvoryTower();

 
  public static IvoryTower getInstance() {
    return INSTANCE;
  }
}
测试
    IvoryTower ivoryTower1 = IvoryTower.getInstance();
    IvoryTower ivoryTower2 = IvoryTower.getInstance();
    LOGGER.info("ivoryTower1={}", ivoryTower1);
    LOGGER.info("ivoryTower2={}", ivoryTower2);
    LOGGER.info("ivoryTower1==ivoryTower2:{}",ivoryTower1==ivoryTower2);
优点:
  1. 线程安全: 饿汉式在类加载时就创建实例,因此在多线程环境下不需要考虑线程安全问题。
  2. 简单直接: 实现简单,代码易于理解。
缺点:
  1. 资源浪费: 饿汉式在类加载时就创建了实例,如果该实例在后续的代码中没有被使用到,会造成资源的浪费。
  2. 不能懒加载: 由于在类加载时就创建实例,因此不能实现延迟加载,如果这个单例在后续的代码中一直没有被用到,就会白白占用内存。

懒汉式线程安全

public final class ThreadSafeLazyLoadedIvoryTower{

  private static ThreadSafeLazyLoadedIvoryTower instance;

  private ThreadSafeLazyLoadedIvoryTower() {
    if (instance == null) {
      instance = this;
    } else {
      throw new IllegalStateException("Already initialized.");
    }
  }

  /**
   * 同步方法保证只被初始化一次
   */
  public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {
    if (instance == null) {
      instance = new ThreadSafeLazyLoadedIvoryTower();
    }

    return instance;
  }

}
测试
 ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 =
        ThreadSafeLazyLoadedIvoryTower.getInstance();
    ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 =
        ThreadSafeLazyLoadedIvoryTower.getInstance();
    LOGGER.info("threadSafeIvoryTower1={}", threadSafeIvoryTower1);
    LOGGER.info("threadSafeIvoryTower2={}", threadSafeIvoryTower2);
    LOGGER.info("threadSafeIvoryTower1==threadSafeIvoryTower2:{}",threadSafeIvoryTower1==threadSafeIvoryTower2);
优点:
  1. 延迟加载: 实例只有在第一次使用时才会被创建,避免了资源的浪费。
  2. 适合多线程环境: 使用同步锁等机制可以保证在多线程环境下也能保持单例的唯一性。
缺点:
  1. 性能低下: 加锁机制导致在并发环境下性能会有所下降,因为每次获取实例都需要进行加锁和解锁操作。
     

枚举式

public enum EnumIvoryTower {

  INSTANCE;

  @Override
  public String toString() {
    return getDeclaringClass().getCanonicalName() + "@" + hashCode();
  }
}
测试
    EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE;
    EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE;
    LOGGER.info("enumIvoryTower1={}", enumIvoryTower1);
    LOGGER.info("enumIvoryTower2={}", enumIvoryTower2);
    LOGGER.info("enumIvoryTower1==enumIvoryTower2:{}",enumIvoryTower1==enumIvoryTower2);
优点:
  1. 线程安全: 枚举类的实例创建是线程安全的,因为枚举类的加载过程是由JVM在类加载阶段进行的,保证了线程安全。
  2. 防止反射攻击: 枚举类的构造方法是私有的,因此无法通过反射机制来创建枚举类的实例。
缺点:
  1. 不支持懒加载: 枚举类在加载时就会创建实例,不支持延迟加载,无法实现懒加载的特性。

双重检查锁

public final class ThreadSafeDoubleCheckLocking {

  private static volatile ThreadSafeDoubleCheckLocking instance;


  private ThreadSafeDoubleCheckLocking() {
    if (instance != null) {
      throw new IllegalStateException("Already initialized.");
    }
  }

  public static ThreadSafeDoubleCheckLocking getInstance() {

    ThreadSafeDoubleCheckLocking result = instance;
    // 第一次检查,此时不加锁,如果不为空,返回实例
    if (result == null) {
      //同步对象
      synchronized (ThreadSafeDoubleCheckLocking.class) {
        
        result = instance;
        //第二次检查,此时虽然没有其他线程进入同步代码块,但是有可能到这里的时候其他线程已经初始化了。因为第一次判断的时候可能其他线程正在初始化,判断完成后正好初始化完成,instance定义为volatile保证了线程对instance的可见性
        if (result == null) {
          instance = result = new ThreadSafeDoubleCheckLocking();
        }
      }
    }
    return result;
  }
}
测试
    ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance();
    LOGGER.info(dcl1.toString());
    ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance();
    LOGGER.info(dcl2.toString());
    LOGGER.info("dcl1==dcl2:{}",dcl1==dcl2);
优点:
  1. 延迟加载: 双重检查锁定允许在需要时延迟初始化单例对象,避免了一开始就创建实例。
  2. 提高性能: 在实例已经被创建的情况下,避免了每次获取实例时都进入同步代码块,提高了性能。
缺点:
  1. 实现复杂: 双重检查锁定的实现相对复杂,需要考虑线程安全、指令重排序等并发问题,容易出错。

静态内部类

public final class InitializingOnDemandHolderIdiom {

  private InitializingOnDemandHolderIdiom() {}


  public static InitializingOnDemandHolderIdiom getInstance() {
    return HelperHolder.INSTANCE;
  }

  private static class HelperHolder {
    private static final InitializingOnDemandHolderIdiom INSTANCE =
        new InitializingOnDemandHolderIdiom();
  }
}
测试
    InitializingOnDemandHolderIdiom demandHolderIdiom =
        InitializingOnDemandHolderIdiom.getInstance();
    LOGGER.info(demandHolderIdiom.toString());
    InitializingOnDemandHolderIdiom demandHolderIdiom2 =
        InitializingOnDemandHolderIdiom.getInstance();
    LOGGER.info(demandHolderIdiom2.toString());
    LOGGER.info("demandHolderIdiom==demandHolderIdiom2:{}",demandHolderIdiom==demandHolderIdiom2);
优点:
  1. 延迟加载: 静态内部类只有在被使用时才会被加载,因此可以实现延迟加载。
  2. 线程安全: 静态内部类在加载时是线程安全的,JVM会保证只加载一次,避免了多线程并发访问的问题。
  3. 简单明了: 实现简单,代码清晰易懂。
缺点:
  1. 不适用于某些场景: 如果单例的实例化需要处理复杂的资源管理或者有复杂的初始化过程,静态内部类可能不够灵活。

 

原型模式(Prototype Pattern)

定义

原型设计模式是一种创建型设计模式,其主要目的是通过克隆现有对象来创建新的对象,而无需显式地使用构造函数。

意图

  • 允许通过克隆已有对象来创建新对象,而无需直接调用构造函数。
  • 提供了一种创建对象的方式,避免了子类化。
  • 减少了对特定类的实例化过程,提高了创建对象的效率。

举例

Person类


import java.io.*;

class Person implements Cloneable,Serializable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }

    public Person deepCopy() {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos;
        try {
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (Person) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
}
测试

通过clone和序列化方式创建原型对象

@Slf4j
public class App {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person("origin",0);
        Person clone = person.clone();
        Person deepCopy = person.deepCopy();

        log.info("person == clone:{}",person==clone);
        log.info("person.name == clone.name:{}",person.getName()==clone.getName());

        log.info("person.name == deepCopy.name:{}",person.getName()==deepCopy.getName());
    }
}
优点:
  1. 减少对象创建时间: 直接克隆对象比使用 new 关键字创建对象更快,因为不需要执行构造函数和初始化步骤。
  2. 减少资源消耗: 避免了重复的初始化工作,节省了系统资源。
  3. 简化对象创建过程: 不需要重新初始化对象,只需克隆即可得到相似的对象。
缺点:
  1. 破坏封装性: 克隆会绕过构造函数的执行,可能破坏类的封装性,导致一些不可预知的问题。
  2. 需要深度拷贝: 如果对象内部包含引用类型的属性,需要进行深度拷贝才能保证每个对象的属性独立性。
  3. 可能导致循环引用问题: 如果对象之间存在循环引用,克隆可能会导致无限循环,需要特殊处理。

原型模式应用

Spring

使用@Scope(value = "prototype") 方式 时,spring将使用原型方式创建bean

建造者模式(Builder Pattern)

定义

建造者模式(Builder Pattern)是一种创建型设计模式,其主要目的是将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。

意图

  • 将一个复杂对象的构建过程抽象出来,使得相同的构建过程可以创建不同的表示。
  • 使得客户端可以构建复杂对象的表示,而不必关心其内部的构建过程。

参与者

  1. Director(指挥者): 负责使用具体建造者(Concrete Builder)的接口来构建产品对象。通常定义了一个构建方法,通过调用具体建造者的方法来构建产品。

  2. Builder(抽象建造者): 定义了构建产品各个部分的抽象接口,通常包括构建产品的方法和返回产品的方法。

  3. ConcreteBuilder(具体建造者): 实现抽象建造者接口,完成具体产品的构建过程。通常包括一个持有产品实例的成员变量,用于保存构建过程中的中间结果。

  4. Product(产品): 表示被构建的复杂对象。通常包含多个部分,其构建过程由具体建造者来完成。

举例

以构建Person为例

类图

构建person


public class Person {

	// 必要参数
	private final int id;
	private final String name;
	// 可选参数
	private final int age;
	private final String sex;
	private final String phone;
	private final String address;
	private final String desc;

	@Override
	public String toString() {
		return "Person{" +
				"id=" + id +
				", name='" + name + '\'' +
				", age=" + age +
				", sex='" + sex + '\'' +
				", phone='" + phone + '\'' +
				", address='" + address + '\'' +
				", desc='" + desc + '\'' +
				'}';
	}

	private Person(Builder builder) {
		this.id = builder.id;
		this.name = builder.name;
		this.age = builder.age;
		this.sex = builder.sex;
		this.phone = builder.phone;
		this.address = builder.address;
		this.desc = builder.desc;
	}

	public static class Builder {
		// 必要参数
		private final int id;
		private final String name;
		// 可选参数
		private int age;
		private String sex;
		private String phone;
		private String address;
		private String desc;

		public Builder(int id, String name) {
			this.id = id;
			this.name = name;
		}

		public Builder age(int val) {
			this.age = val;
			return this;
		}

		public Builder sex(String val) {
			this.sex = val;
			return this;
		}

		public Builder phone(String val) {
			this.phone = val;
			return this;
		}

		public Builder address(String val) {
			this.address = val;
			return this;
		}

		public Builder desc(String val) {
			this.desc = val;
			return this;
		}

		public Person build() {
			return new Person(this);
		}
	}
}
测试
	public static void main(String[] args) {
        Person person = new Person.Builder(1, "张三").age(18).sex("男").desc("测试使用builder模式").build();
        log.info("person info:{}",person);
	}
优点:
  1. 分离构建过程和表示: 建造者模式将一个复杂对象的构建过程和它的表示分离开,使得同样的构建过程可以创建不同的表示,提高了灵活性。
  2. 更好的封装性: 客户端无需关心产品的内部构建过程,只需要关心建造者的接口,使得产品的内部结构对客户端透明,达到了更好的封装性。
  3. 便于扩展: 可以更容易地扩展具体建造者或改变具体产品的内部表示,而对客户端代码的影响较小。
  4. 更好的复用性: 可以使用相同的构建过程来构建不同的产品对象,提高了构建过程的复用性。
  5. 更好的控制产品的构建过程: 指挥者可以根据需要调用不同的具体建造者来构建产品,控制产品的构建过程。
缺点:
  1. 增加了系统的复杂性: 引入了多个新的角色和接口,增加了系统的复杂性,特别是在产品对象的部分或属性较多的情况下。
  2. 不适用于简单对象的构建: 如果产品对象的构建过程很简单,那么使用建造者模式可能会显得繁琐,不适合使用。
  3. 增加了代码量: 引入了具体建造者和指挥者,可能会增加系统的代码量。
  4. 客户端需要知道具体建造者: 客户端在使用建造者模式时,需要知道具体建造者的类名,这违反了依赖倒置原则,增加了客户端与具体建造者的耦合度。

建造者模式应用

StringBuilder

SpringSecurity

 public SecurityFilterChain securityFilterChain(HttpSecurity http,
                                                   ClientRegistrationRepository clientRegistrationRepository) throws Exception {
        http
                .authorizeHttpRequests(authorize ->
                        authorize
                                .requestMatchers("/logged-out","/oauth2/token/**").permitAll()
                                .anyRequest().authenticated()
                )
                .oauth2Login(oauth2Login ->
                        oauth2Login.loginPage("/oauth2/authorization/messaging-client-oidc"))
                .oauth2Client(withDefaults())

                .logout(logout ->
                        logout.logoutSuccessHandler(oidcLogoutSuccessHandler(clientRegistrationRepository)));
        return http.build();
    }

 

WebClient

    public WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        // @formatter:off
        return WebClient.builder()
                .apply(oauth2Client.oauth2Configuration())
                .build();
        // @formatter:on
    }

 

行为型模式

这些模式关注对象之间的通信和交互,旨在解决对象之间的责任分配和算法的封装。

设计模式-命令模式(Command)

 

命令模式的目的

    将服务请求封装成对象,从而可以不同的方式对请求进行操作。

    它使client可以调用命令但不需要了解命令的执行细节。并且可以对命令进行修改而无需影响调用命令的client程序。

命令模式的结构

    Command

     声明执行操作的接口

    ConcreteCommand

    定义Receiver对象和动作的绑定

    通过调用Receiver的相应操作实现Execute()方法

    Client

     创建ConcreteCommand对象从而设置相应的receiver

    Invoker

     要求命令执行用户请求

    Receiver

     负责解析用户的请求并执行相应的操作

命令模式的类图

下面看一个例子(例子来源:https://github.com/iluwatar/java-design-patterns

男巫(wizard)施加命令(command)给小怪。二具体的命令(invisibility spell,shrink spell)是让小怪(target)不可见或者变大或变小及取消施加命令和重新施加。

调用者

public class Wizard {

  private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);

  private Deque<Command> undoStack = new LinkedList<>();
  private Deque<Command> redoStack = new LinkedList<>();

  public Wizard() {
    // comment to ignore sonar issue: LEVEL critical
  }

  /**
   * Cast spell
   */
  public void castSpell(Command command, Target target) {
    LOGGER.info("{} casts {} at {}", this, command, target);
    command.execute(target);
    undoStack.offerLast(command);
  }

  /**
   * Undo last spell
   */
  public void undoLastSpell() {
    if (!undoStack.isEmpty()) {
      Command previousSpell = undoStack.pollLast();
      redoStack.offerLast(previousSpell);
      LOGGER.info("{} undoes {}", this, previousSpell);
      previousSpell.undo();
    }
  }

  /**
   * Redo last spell
   */
  public void redoLastSpell() {
    if (!redoStack.isEmpty()) {
      Command previousSpell = redoStack.pollLast();
      undoStack.offerLast(previousSpell);
      LOGGER.info("{} redoes {}", this, previousSpell);
      previousSpell.redo();
    }
  }

  @Override
  public String toString() {
    return "Wizard";
  }

 

抽象的命令类 

public abstract class Command {

  public abstract void execute(Target target);

  public abstract void undo();

  public abstract void redo();

  @Override
  public abstract String toString();

}

 

命令InvisibilitySpell 隐身

public class InvisibilitySpell extends Command {

  private Target target;

  @Override
  public void execute(Target target) {
    target.setVisibility(Visibility.INVISIBLE);
    this.target = target;
  }

  @Override
  public void undo() {
    if (target != null) {
      target.setVisibility(Visibility.VISIBLE);
    }
  }

  @Override
  public void redo() {
    if (target != null) {
      target.setVisibility(Visibility.INVISIBLE);
    }
  }

  @Override
  public String toString() {
    return "Invisibility spell";
  }
}

 

命令类ShrinkSpell 变小,收缩

public class ShrinkSpell extends Command {

  private Size oldSize;
  private Target target;

  @Override
  public void execute(Target target) {
    oldSize = target.getSize();
    target.setSize(Size.SMALL);
    this.target = target;
  }

  @Override
  public void undo() {
    if (oldSize != null && target != null) {
      Size temp = target.getSize();
      target.setSize(oldSize);
      oldSize = temp;
    }
  }

  @Override
  public void redo() {
    undo();
  }

  @Override
  public String toString() {
    return "Shrink spell";
  }
}

 

public abstract class Target {

  private static final Logger LOGGER = LoggerFactory.getLogger(Target.class);

  private Size size;

  private Visibility visibility;

  public Size getSize() {
    return size;
  }

  public void setSize(Size size) {
    this.size = size;
  }

  public Visibility getVisibility() {
    return visibility;
  }

  public void setVisibility(Visibility visibility) {
    this.visibility = visibility;
  }

  @Override
  public abstract String toString();

  /**
   * Print status
   */
  public void printStatus() {
    LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
  }
}

 

小怪Goblin ,被施加命令的对象

public class Goblin extends Target {

  public Goblin() {
    setSize(Size.NORMAL);
    setVisibility(Visibility.VISIBLE);
  }

  @Override
  public String toString() {
    return "Goblin";
  }

}

 

客户端调用 通过施加不同的命令,取消命令或重新施加命令,然后打印目标的状态查看命令是否施加成功。

public class App {

  /**
   * Program entry point
   * 
   * @param args command line args
   */
  public static void main(String[] args) {
    Wizard wizard = new Wizard();
    Goblin goblin = new Goblin();

    goblin.printStatus();

    wizard.castSpell(new ShrinkSpell(), goblin);
    goblin.printStatus();

    wizard.castSpell(new InvisibilitySpell(), goblin);
    goblin.printStatus();

    wizard.undoLastSpell();
    goblin.printStatus();

    wizard.undoLastSpell();
    goblin.printStatus();

    wizard.redoLastSpell();
    goblin.printStatus();

    wizard.redoLastSpell();
    goblin.printStatus();
  }
}

命令模式总结

  • 命令模式实现了调用操作的对象与具体实现操作的对象之间的解耦
  • 命令可以像其他对象一样被操作和扩展
  • 命令可以被组合为一个复合命令
     

 

策略模式(Strategy Pattern)

定义

策略模式允许在运行时选择算法的行为。它允许你定义一系列算法,并将每个算法封装在单独的类中,使它们可以互相替换。这样,客户端可以在不改变其使用方式的情况下选择要使用的算法。这种模式有助于提高代码的灵活性和可维护性,因为它将不同的算法逻辑与主要的业务逻辑分离开来。

意图

使算法的选择与使用者(客户端)的解耦。它允许在不修改客户端代码的情况下动态地改变或切换算法,通过封装不同的算法,使得它们可以相互替换。这样的设计使得系统更灵活、可扩展,并且更容易维护,因为新增、修改或删除算法不会对客户端造成影响。策略模式还有助于避免代码中大量的条件判断语句,提高了代码的可读性和可维护性。

参与者(角色)​​​​​​

  • Context(上下文):上下文是策略模式的主要组成部分,它持有一个对策略接口的引用,并在运行时可以切换不同的具体策略。上下文通常会将客户端请求委派给所选的策略对象执行。

  • Strategy(策略):策略是一个接口或抽象类,它定义了一系列算法的通用接口。所有具体策略类都必须实现这个接口,确保它们拥有相同的行为。

  • Concrete Strategies(具体策略):具体策略是实现了策略接口的具体类。每个具体策略类都封装了一个特定的算法。

举例

已使用不同的支付方式为例

类图:

这个例子中对应类的角色如下:

  • Context(上下文):PayContext

  • Strategy(策略):PayStrategy

  • Concrete Strategies(具体策略):AliPay,CreditCardPay,WeiXinPay

 

PayStrategy接口

public interface PayStrategy {
     void pay(double amount);
}

 

PayContext类

public class PayContext {

    private PayStrategy payStrategy;


    public PayContext(PayStrategy payStrategy) {
        this.payStrategy = payStrategy;
    }

    public void changeStrategy(PayStrategy payStrategy) {
        this.payStrategy = payStrategy;
    }


    public  void execute(double amount){
        payStrategy.pay(amount);
    }
}

 

CreditCardPay支付类

@Slf4j
public class CreditCardPay implements PayStrategy {

    @Override
    public void pay(double amount) {
        log.info("使用信用卡支付了:{}元",amount);
    }
}

 

WeiXinPay类

@Slf4j
public class WeiXinPay implements PayStrategy {

    @Override
    public void pay(double amount) {
        log.info("使用微信支付了:{}元",amount);
    }
}

 

AliPay支付类

@Slf4j
public class AliPay implements PayStrategy {


    @Override
    public void pay(double amount) {
      log.info("使用支付宝支付了:{}元",amount);
    }
}

 

测试

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

        AliPay aliPay = new AliPay();
        PayContext context = new PayContext(aliPay);
        context.execute(100);
        context.changeStrategy(new WeiXinPay());
        context.execute(200);
        context.changeStrategy(new CreditCardPay());
        context.execute(300);
    }
}

 

优点

  1. 灵活性: 策略模式允许在运行时动态切换算法或策略,无需修改客户端代码,提供了灵活性和可维护性。

  2. 可扩展性: 新的策略可以很容易地添加到系统中,不会影响现有的代码结构,从而提供了可扩展性。

  3. 代码复用: 通过策略模式,不同的算法逻辑被封装在独立的策略类中,可以在多个地方重复使用,促进了代码复用。

  4. 减少条件判断: 策略模式可以避免大量的条件判断语句,提高了代码的可读性和可维护性。

缺点

  1. 类数量增加: 每个策略都需要一个单独的类,可能会导致类的数量增加,尤其是当策略较多时,会增加代码复杂度。

  2. 客户端必须知道所有的策略: 客户端必须了解所有可用的策略并选择使用哪一个,这可能会增加客户端的复杂性。

  3. 上下文与策略的耦合: 在某些情况下,上下文对象与特定的策略类紧密耦合,可能导致修改上下文对象以适应新策略的引入。

策略模式的应用

springSecuriy认证策略。spring-ouauth2-原理

 

//设置不同的认证策略

    void configure(HttpSecurity httpSecurity) {
        AuthenticationManager authenticationManager = (AuthenticationManager)httpSecurity.getSharedObject(AuthenticationManager.class);
        AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity);
        OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter(authenticationManager, authorizationServerSettings.getAuthorizationEndpoint());
        List<AuthenticationConverter> authenticationConverters = createDefaultAuthenticationConverters();
        if (!this.authorizationRequestConverters.isEmpty()) {
            authenticationConverters.addAll(0, this.authorizationRequestConverters);
        }

        this.authorizationRequestConvertersConsumer.accept(authenticationConverters);
        authorizationEndpointFilter.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters));
        if (this.authorizationResponseHandler != null) {
            authorizationEndpointFilter.setAuthenticationSuccessHandler(this.authorizationResponseHandler);
        }

        if (this.errorResponseHandler != null) {
            authorizationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler);
        }

        if (StringUtils.hasText(this.consentPage)) {
            authorizationEndpointFilter.setConsentPage(this.consentPage);
        }

        if (this.sessionAuthenticationStrategy != null) {
            authorizationEndpointFilter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy);
        }

        httpSecurity.addFilterBefore((Filter)this.postProcess(authorizationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class);
    }

 

模版模式(Template Pattern)

定义

模板方法是一种行为设计模式,它定义了一个算法的骨架,将某些步骤的具体实现推迟到子类中。这种模式允许在不改变算法整体结构的情况下,通过子类重写方法来修改算法的某些步骤。

意图

定义算法骨架: 定义一个算法的主要结构,将具体步骤的实现延迟到子类中。

允许扩展特定步骤: 允许子类根据需要重写或扩展父类中的特定步骤,同时保持整体算法结构不变。

参与者(角色)​​​​​​

  • 抽象类(Abstract Class):定义算法的抽象骨架,包含算法中的各个步骤的框架和顺序,但留下某些步骤的实现细节由子类实现。定义了模板方法,作为算法骨架的整体结构。
  • 具体类(Concrete Class):实现抽象类中的抽象方法,完成具体的算法细节。实现父类中定义的模板方法,覆盖或扩展算法中的特定步骤。

举例

已创建订单为例

类图:

 

这个例子中对应类的角色如下:

  • 抽象类(Abstract Class):OrderProcessing

  • 具体类(Concrete Class):OnlineShoppingOrder,RestaurantOrder,EcommerceOrder

 

OrderProcessing类

public abstract class OrderProcessing {

    public  void processOrder(String productName,int amount){
       boolean flag= validateOrder(productName,amount);
       if(flag){
           Order order= createOrder(productName,amount);
           dispatchOrder(order);
           compleateOrder(order);
       }

    }


    protected abstract void compleateOrder(Order order);

    protected abstract void dispatchOrder(Order order);

    protected abstract Order createOrder(String productName ,int amount) ;

    protected abstract boolean validateOrder(String productName,int amount) ;
}

 

OnlineShoppingOrder类

@Slf4j
public class OnlineShoppingOrder extends OrderProcessing{



    @Override
    protected void compleateOrder(Order order) {
        log.info("网购订单:{} 已完成!",order.getOrderId());
    }

    @Override
    protected void dispatchOrder(Order order) {
        log.info("网购订单:{} 运输中",order.getOrderId());
    }

    @Override
    protected Order createOrder(String productName ,int amount) {
        Order order  = new Order();
        order.setOrderId(RandomUtils.nextLong()+"");
        order.setProcudtName(productName);
        order.setProductAmount(amount);
        log.info("网购订单创建完成:{}",order.getOrderId());
        return order;
    }

    @Override
    protected boolean validateOrder(String productName ,int amount) {
        OrderService orderService = new OrderService();
        int stock = orderService.getAmount(productName);
        if(stock>=amount){
            return true;
        }
        log.error("产品名称:{},库存不足,剩余:{}",productName,stock);
        return false;
    }
}

 

EcommerceOrder支付类

@Slf4j
public class EcommerceOrder extends OrderProcessing{



    @Override
    protected void compleateOrder(Order order) {
        log.info("电子商务订单:{} 已完成!",order.getOrderId());
    }

    @Override
    protected void dispatchOrder(Order order) {
        log.info("电子商务订单: {} 运输中!",order.getOrderId());
    }

    @Override
    protected Order createOrder(String productName ,int amount) {
        Order order  = new Order();
        order.setOrderId(RandomUtils.nextLong()+"");
        order.setProcudtName(productName);
        order.setProductAmount(amount);
        log.info("电子商务订单创建完成:{}",order.getOrderId());
        return order;
    }

    @Override
    protected boolean validateOrder(String productName ,int amount) {
        OrderService orderService = new OrderService();
        int stock = orderService.getAmount(productName);
        if(stock>=amount){
            return true;
        }
        log.error("产品名称:{},库存不足,剩余:{}",productName,stock);
        return false;
    }
}

 

RestaurantOrder类

@Slf4j
public class RestaurantOrder extends OrderProcessing{



    @Override
    protected void compleateOrder(Order order) {
        log.info("超市订单:{} 已完成!",order.getOrderId());
    }

    @Override
    protected void dispatchOrder(Order order) {
        log.info("超市订单:{} 运输中!",order.getOrderId());
    }

    @Override
    protected Order createOrder(String productName ,int amount) {
        Order order  = new Order();
        order.setOrderId(RandomUtils.nextLong()+"");
        order.setProcudtName(productName);
        order.setProductAmount(amount);
        log.info("超市订单创建完成:{}",order.getOrderId());
        return order;
    }

    @Override
    protected boolean validateOrder(String productName ,int amount) {
        OrderService orderService = new OrderService();
        int stock = orderService.getAmount(productName);
        if(stock>=amount){
            return true;
        }
        log.error("产品名称:{},库存不足,剩余:{}",productName,stock);
        return false;
    }
}

 

OrderService支付类

@Service
public class OrderService {

    static Map<String,Integer> products = new HashMap<>();

    static {
        products.put("iphone15",10);
        products.put("MacBookPro",2);
        products.put("giftCard",12);
    }


    public int getAmount(String productName){
        return products.get(productName);
    }
}

 

测试

public class App {
    public static void main(String[] args) {
        OrderProcessing orderProcessing  = new OnlineShoppingOrder();
        orderProcessing.processOrder("iphone15",17);
        orderProcessing  = new RestaurantOrder();
        orderProcessing.processOrder("MacBookPro",1);
        orderProcessing  = new EcommerceOrder();
        orderProcessing.processOrder("giftCard",2);
    }
}

 

优点

  1. 代码复用性: 共享算法骨架,避免重复代码,促进代码复用。
  2. 扩展性: 允许子类实现或重写特定步骤,使得算法的部分行为可以灵活扩展。
  3. 维护性: 遵循开闭原则,允许在不修改算法整体结构的情况下修改部分步骤。

缺点

  1. 限制扩展性: 如果父类中的某些步骤对于所有子类都是固定的,可能会限制部分子类的扩展。
  2. 过多细节暴露: 子类必须了解父类的实现细节,可能导致对父类依赖过高。

策略模式的应用

Springboot 中 RestTemplate,RedisTemplate,RabbitTemplate等都是用模版方法。这里没有具体体现抽象类,可以理解为只有一个实现类的模版方法设计。

Spring Ioc


AbstractApplicationContext 定义了refresh的方法 这个方法相是算法的骨架

public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");

			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);
				beanPostProcess.end();

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
				contextRefresh.end();
			}
		}
	}

 

obtainFreshBeanFactory中两个方法都是抽象方法,子类实现具体刷新和获取beanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    return getBeanFactory();
}

迭代器模式(Iterator Pattern)

定义

迭代器模式是一种行为设计模式,它允许你按顺序访问一个聚合对象中的元素,而无需暴露集合的内部结构。迭代器提供了一种标准的方式来遍历聚合对象,使得可以在不关心底层实现的情况下遍历集合。

意图

通过将遍历算法和集合分离,提供了一种通用的方式来访问集合中的元素。

提供一种方法,用于按顺序访问聚合对象的元素,而不暴露其底层表示。

参与者(角色)​​​​​​

  1. 抽象迭代器(Iterator):定义了访问和遍历元素的接口。
  2. 具体迭代器(ConcreteIterator):实现了抽象迭代器接口,负责实现对集合的具体遍历。
  3. 抽象聚合(Aggregate):定义创建迭代器对象的接口。
  4. 具体聚合(ConcreteAggregate):实现了创建迭代器的接口,这个类会创建对应的具体迭代器。

举例

从百宝箱中获取不同的物品

类图:

 

这个例子中对应类的角色如下:

  • 抽象迭代器(Iterator):Iterator
  • 具体迭代器(ConcreteIterator):TreasureChest
  • 抽象聚合(Aggregate):TreasureChest (这里没有严格按抽象类定义)
  • 具体聚合(ConcreteAggregate):TreasureChestItemIterator

Iterator类接口

public interface Iterator<T> {

  boolean hasNext();

  T next();
}

 

元素类

public class Item {

  private ItemType type;
  private final String name;

  public Item(ItemType type, String name) {
    this.setType(type);
    this.name = name;
  }

  @Override
  public String toString() {
    return name;
  }

  public ItemType getType() {
    return type;
  }

  public final void setType(ItemType type) {
    this.type = type;
  }
}

 

元素类型 ItemType

public enum ItemType {

  ANY("任意"), WEAPON("武器"), RING("戒指"), POTION("药水");

  private String name;

  ItemType(String name) {
    this.name=name;
  }

  public String getName() {
    return name;
  }
}

 

百宝箱TreasureChest

public class TreasureChest {

  private final List<Item> items;

  /**
   * Constructor.
   */
  public TreasureChest() {
    items = List.of(
            new Item(ItemType.POTION, "勇气药水"),
            new Item(ItemType.RING, "阴影之戒"),
            new Item(ItemType.POTION, "智慧药水"),
            new Item(ItemType.POTION, "鲜血药水"),
            new Item(ItemType.WEAPON, "银剑 +1"),
            new Item(ItemType.POTION, "锈蚀药水"),
            new Item(ItemType.POTION, "愈合药水"),
            new Item(ItemType.RING, "护甲之戒"),
            new Item(ItemType.WEAPON, "钢制战斧"),
            new Item(ItemType.WEAPON, "毒性匕首"));

  }

  public Iterator<Item> iterator(ItemType itemType) {
    return new TreasureChestItemIterator(this, itemType);
  }

  /**
   * Get all items.
   */
  public List<Item> getItems() {
    return new ArrayList<>(items);
  }

}

 

 具体迭代器TreasureChestItemIterator

public class TreasureChestItemIterator implements Iterator<Item> {

  private final TreasureChest chest;
  private int idx;
  private final ItemType type;

  /**
   * Constructor.
   */
  public TreasureChestItemIterator(TreasureChest chest, ItemType type) {
    this.chest = chest;
    this.type = type;
    this.idx = -1;
  }

  @Override
  public boolean hasNext() {
    return findNextIdx() != -1;
  }

  @Override
  public Item next() {
    idx = findNextIdx();
    if (idx != -1) {
      return chest.getItems().get(idx);
    }
    return null;
  }

  private int findNextIdx() {
    var items = chest.getItems();
    var tempIdx = idx;
    while (true) {
      tempIdx++;
      if (tempIdx >= items.size()) {
        tempIdx = -1;
        break;
      }
      if (type.equals(ItemType.ANY) || items.get(tempIdx).getType().equals(type)) {
        break;
      }
    }
    return tempIdx;
  }
}

 

测试

@Slf4j
public class App {

  private static final TreasureChest TREASURE_CHEST = new TreasureChest();

  private static void demonstrateTreasureChestIteratorForType(ItemType itemType) {
    log.info("------------------------");
    log.info("迭代器类型 {} :",itemType.getName());
    var itemIterator = TREASURE_CHEST.iterator(itemType);
    while (itemIterator.hasNext()) {
      log.info(itemIterator.next().toString());
    }
  }



  /**
   * Program entry point.
   *
   * @param args command line args
   */
  public static void main(String[] args) {
    demonstrateTreasureChestIteratorForType(RING);
    demonstrateTreasureChestIteratorForType(POTION);
    demonstrateTreasureChestIteratorForType(WEAPON);
    demonstrateTreasureChestIteratorForType(ANY);

  }
}

 

优点

  1. 简化遍历:提供了统一的遍历接口,简化了集合元素的遍历方式。
  2. 解耦合:迭代器将集合与遍历方式分离,使得集合的实现细节不暴露给客户端代码。
  3. 支持多种遍历:可以实现多种不同类型的迭代器,适应不同的遍历需求。

缺点

  1. 增加了复杂度:引入迭代器会增加代码的复杂度,尤其是在集合元素结构发生变化时,可能需要修改迭代器实现。
  2. 遍历性能不一定高:有些情况下,使用迭代器进行遍历可能会比直接访问元素稍慢,尤其是对于一些底层数据结构的迭代实现。

迭代器模式的应用

java.util.Iterator

委派设计模式(delegation)

定义

委派模式是一种结构型设计模式,它允许一个对象(称为委托对象)将某些任务委派给其他对象(称为委托类),以便自己专注于更高级别的工作。在委派模式中,委托对象负责任务的分发和协调,但实际的工作由其他对象来执行。

意图

  • 将对象在外部表现出特定行为的责任委托给关联的对象。

参与者(角色)

  • 委托对象(Delegator): 负责分发任务的对象,它将任务委托给其他对象来执行。

  • 委托接口(Delegate Interface): 委派对象所实现的接口或抽象类,定义了委托对象可以委派的任务。

  • 委托类(Delegate Class): 实际执行任务的对象,它实现了委托接口,并负责完成委派对象指派的具体任务。

举例

以不同的打印机为例

类图:

这个例子中对应类的角色如下:

  • 委托对象(Delegator): PrinterDelegator

  • 委托接口(Delegate Interface): Printer

  • 委托类(Delegate Class): CanonPrinter,EpsonPrinter,HpPrinter

Printer打印机,执行打印任务

/**
 打印机
 */
public interface Printer {

  void print(final String message);
}

 

HpPrinter

@Slf4j
public class HpPrinter implements Printer {

  /**
   * {@inheritDoc}
   */
  @Override
  public void print(String message) {
    log.info("惠普打印机 : {}", message);
  }

}

 

EpsonPrinter

@Slf4j
public class EpsonPrinter implements Printer {

  /**
   * {@inheritDoc}
   */
  @Override
  public void print(String message) {
    log.info("爱普生打印机 : {}", message);
  }

}

 

PrinterDelegator委派对象

 

public class PrinterDelegator implements Printer {

  private  Printer printer;

  public PrinterDelegator(Printer printer) {
    this.printer = printer;
  }

  public void setPrinter(Printer printer) {
    this.printer = printer;
  }

  @Override
  public void print(String message) {
    printer.print(message);
  }
}

测试

public class App {

  private static final String MESSAGE_TO_PRINT = "委派给不同的打印机";

  public static void main(String[] args) {
    PrinterDelegator printerDelegator = new PrinterDelegator(new HpPrinter());

    printerDelegator.print(MESSAGE_TO_PRINT);
    printerDelegator.setPrinter(new CanonPrinter());
    printerDelegator.print(MESSAGE_TO_PRINT);
    printerDelegator.setPrinter(new EpsonPrinter());
    printerDelegator.print(MESSAGE_TO_PRINT);
  }

}

 

优点

  1. 降低耦合性: 委派对象与实际执行任务的对象解耦,使得两者之间的关系更加灵活。

  2. 集中控制: 委派对象负责任务的调度和分发,可以实现更灵活的任务管理和调度策略。

  3. 提高复用性: 实现了委派接口的多个委派类可以复用相同的任务执行逻辑。

缺点

  1. 复杂性增加: 委派模式引入了额外的类和对象,增加了代码的复杂性和维护成本。
  2. 灵活性受限: 委派模式在某种程度上限制了灵活性,因为它需要明确指定委派关系,导致扩展时需要修改代码。

代理模式的应用

nacos

客户端注册时,根据配置不同使用grpc或着http请求方式

 

 

 

 

备忘录模式(Memento Pattern)

定义

备忘录模式是一种行为设计模式,它允许在不暴露对象实现细节的情况下,捕获和恢复对象之前的状态。这种模式通常用于需要保存和恢复对象状态的情况,而不破坏其封装性。它允许在不破坏封装性的前提下,将对象的状态保存在外部,并在需要时将对象恢复到之前的状态。

意图

在不违反封装性的情况下,捕获并将对象的内部状态外部化,以便以后可以将对象恢复到这个状态。

参与者(角色)​​​​​​

  • Originator(发起人):负责创建一个备忘录对象,记录当前内部状态,也可以从备忘录中恢复状态。
  • Memento(备忘录):存储原发器的内部状态。通常只允许原发器访问其内部状态。
  • Caretaker(管理者):负责保管备忘录对象,但不会对其进行操作或者检查其内容。其主要目的是存储和提供备忘录。

举例

已mac pages 文稿的撤销重做为例

类图:

 

这个例子中对应类的角色如下:

  • Originator(发起人)PagesEditor
  • Memento(备忘录)PagesMemento
  • Caretaker(管理者)PagesCareTaker

 

PagesCareTaker类

public class PagesCareTaker {
    public List<PagesMemento> records = new ArrayList<>();
    public Stack<PagesMemento>  undo  = new Stack<>();

    public  void record(PagesMemento record){
        records.add(record);
    }

    public  void remove(PagesMemento record){
        records.remove(record);
        undo.push(record);
    }

    public String getCurrPagesDoc(){
        return  records.stream().map(t->t.getText()).collect(Collectors.joining("  "));
    }

    public PagesMemento getUndo() {
        return undo.pop();
    }
}

 

PagesEditor类

public class PagesEditor {

    private final PagesCareTaker pagesCareTaker;
    private String text;

    public PagesEditor(PagesCareTaker pagesCareTaker) {
        this.pagesCareTaker =pagesCareTaker;
    }

    public PagesMemento save( ){
        PagesMemento pagesMemento = new PagesMemento(text);
        pagesCareTaker.record(pagesMemento);
        return  pagesMemento;
    }

    public void redo(PagesMemento pagesMemento){
        pagesCareTaker.record(pagesMemento);
    }

    public void undo(PagesMemento pagesMemento){
        pagesCareTaker.remove(pagesMemento);
    }

    public void setText(String text) {
        this.text = text;
    }
}

 

PagesMemento类

public class PagesMemento {
    private final String text;

    public PagesMemento(String text) {
        this.text = text;
    }

    public String getText() {
        return text;
    }
}

 

测试

@Slf4j
public class App {
    public static void main(String[] args) {
        PagesCareTaker pagesCareTaker = new PagesCareTaker();
        PagesEditor pagesEditor = new PagesEditor(pagesCareTaker);
        pagesEditor.setText("静夜思");
        pagesEditor.save();
        log.info("第一次文档编辑后文档信息:{}",pagesCareTaker.getCurrPagesDoc());

        pagesEditor.setText("床前明月光,疑是地上霜。");
        PagesMemento second = pagesEditor.save();
        log.info("第二次文档编辑后文档信息:{}",pagesCareTaker.getCurrPagesDoc());

        pagesEditor.undo(second);
        log.info("撤销第二次编辑后文档信息:{}",pagesCareTaker.getCurrPagesDoc());

        //由于不小心撤销了第二次的编辑,所以重新恢复
        pagesEditor.redo(pagesCareTaker.getUndo());
        log.info("恢复第二次的编辑后文档信息:{}",pagesCareTaker.getCurrPagesDoc());

        pagesEditor.setText("举头望明月,低头思故乡。");
        pagesEditor.save();
        log.info("最终的文档信息:{}",pagesCareTaker.getCurrPagesDoc());

    }
}

 

优点

  1. 状态保存和恢复:允许在不破坏对象封装的情况下保存和恢复对象的状态。
  2. 简化原始对象:使得原始对象可以专注于它自身的职责,状态管理交由备忘录负责。
  3. 轻松实现撤销和重做功能:可以轻松地实现撤销和重做功能,因为可以保存不同的对象状态。

缺点

  1. 资源消耗:如果备忘录频繁地被创建和保持,可能会消耗大量内存或存储空间。
  2. 复杂性增加:如果需要保存的状态很多,管理备忘录可能会变得复杂。

备忘录模式的应用

Spring Web Flow

在 Spring 框架中的 Spring Web Flow 中看到备忘录模式的应用。Spring Web Flow 是 Spring 项目的一部分,结合了 Spring MVC,用于构建复杂的 Web 流程。显然,这是备忘录模式的一个理想应用场景。Spring Web Flow 通过 StateManageableMessageContext 接口实现了备忘录模式。通过使用这个接口,你可以使用 Spring Web Flow 将应用恢复到先前的状态。

nacos

nacos中的NamingGrpcRedoService

空对象模式(Null Object Pattern)

定义

空对象用于创建一个代表空或无效对象的实例,而无需使用 null。这个模式允许在对象不存在或不可用时提供默认行为,同时避免了对空引用的检查。

意图

大多数面向对象的编程语言(如Java或C#)中,引用可能为null。在调用任何方法之前,需要检查这些引用确保它们不是null,因为通常无法在null引用上调用方法。而不是使用null引用来表示对象的缺失(例如,一个不存在的客户),可以使用实现了期望接口但其方法体为空的对象。这种方法的优势在于相比于一个工作中的默认实现,空对象是非常可预测的,没有任何副作用:它什么都不做。

参与者(角色)​​​​​​

  • 抽象类或接口(Abstract Class or Interface): 定义了具体实现对象的接口或抽象类。

  • 具体对象(Concrete Objects): 实现了抽象类或接口的具体类。

  • 空对象(Null Object): 实现了抽象类或接口,但其方法体为空。通常在需要引用对象但不想或不能使用实际对象时使用空对象,以确保不会出现Null引用异常。

举例

已创建订单为例

类图:

 

这个例子中对应类的角色如下:

  • 抽象类或接口(Abstract Class or Interface): Order

  • 具体对象(Concrete Objects): RealOrder

  • 空对象(Null Object): NullOrder

 

Order类 ,抽象类

public abstract class Order {

    String orderId;

    void process(String orderId) {

    }

}

 

RealOrder类

@Slf4j
public class RealOrder extends Order{
    public RealOrder(String orderId) {
        this.orderId=orderId;
    }

    @Override
    public void process(String orderId) {
            log.info("订单处理成功,订单id:{}",orderId);
    }
}

 

NullOrder空对象类

@Slf4j
public class NullOrder extends Order{

    public NullOrder(String orderId) {
        this.orderId=orderId;
    }

    @Override
    public void process(String orderId) {
        log.info("订单不存在,订单id:{}",orderId);

    }
}

 

OrderFactory订单工厂

public  class OrderFactory {
    static List datas = List.of("1","3","5","6");


    static Order  getOrder(String orderId){
        boolean match = datas.stream().anyMatch(o -> o.equals(orderId));
        if(match)
            return  new RealOrder(orderId);
        else
            return new NullOrder(orderId);
    }
}

 

测试

对与不存在订单也可以和存在的定义一样处理,而不用单独做判断

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

        List<Order> orders = new ArrayList();

        orders.add(OrderFactory.getOrder("1"));
        orders.add(OrderFactory.getOrder("3"));
        orders.add(OrderFactory.getOrder("7"));

        orders.forEach(o -> o.process(o.orderId));
    }
}

 

优点

  1. 避免空引用异常: 空对象确保在需要对象引用但没有实际对象时不会出现空引用异常。

  2. 简化代码: 可以减少在代码中进行空值检查的需求,简化了对空对象的处理。

  3. 预测性: 空对象的行为是明确的且无副作用,因此在使用空对象时更可预测。

  4. 隐藏实现细节: 可以隐藏在特定情况下的空对象实现细节,使使用者无需关注特定对象是否为空。

  5. 默认行为: 可以提供默认行为,使系统在没有实际对象时有一个可行的默认实现。

缺点

  1. 引入了更多的类,可能导致系统变得复杂

空对象模式的应用

java Optional

public static void main(String[] args) {
        Person person =null;
        Optional<Person> optional = Optional.ofNullable(person);
        log.info("option is null:{}",optional.isEmpty());
        log.info("option is persent:{}",optional.isPresent());
        Person person1 = optional.orElse(new Person());
        person1.setName("zlennon");
        log.info("defalut value:{}",person1.toString());
}

 

 

 

中介者模式(Mediator Pattern)

定义

定义一个封装了一组对象如何交互的对象。中介者通过防止对象直接显式地引用彼此,促进了松散耦合,并允许您独立地改变它们之间的交互方式。

意图

中介者模式旨在通过减少对象之间的直接通信,促进对象之间的松散耦合,从而提高系统的可维护性和可扩展性。它允许多个对象之间通过一个中介者对象进行通信,而不是直接相互引用,从而减少耦合性,简化对象之间的交互。

参与者(角色)​​​​​​

  • 中介者(Mediator): 负责定义对象之间交互的接口,以及实现这些接口来协调和管理对象之间的通信。
  • 具体中介者(Concrete Mediator): 实现了中介者接口,负责实际协调和管理对象之间的通信。
  • 同事对象(Colleague): 持有对中介者对象的引用,并通过中介者对象来实现与其他同事对象之间的交互。
  • 具体同事对象(Concrete Colleague): 实现了同事接口,并通过中介者对象与其他同事对象进行通信。

举例

已使用im发送群聊消息为例

类图:

这个例子中对应类的角色如下:

  • 中介者(Mediator): Mediator
  • 具体中介者(Concrete Mediator): IMServerMediator
  • 同事对象(Colleague): Client
  • 具体同事对象(Concrete Colleague):AndroidClient,IOSClient

 

Mediator中介者接口

public interface Mediator {
    void sendMsg(Message message, Group group);
}

 

IMServerMediator具体中介者类

public class IMServerMediator implements Mediator {


    Map<Group, Map<String, Queue<Message>>> groupMessage = new ConcurrentHashMap<>();



    public void sendMsg(Message msg, Group group) {
        Map<String, Queue<Message>> userMsg = this.groupMessage.getOrDefault(group, new ConcurrentHashMap<>());

        //排除没有登陆的用户,添加到离线消息中
        Set<User> offlineUsers = group.users.stream().filter(u -> !u.isLogin).collect(Collectors.toSet());

        offlineUsers.forEach(u -> {
            Queue<Message> stringQueue = userMsg.getOrDefault(u.userId, new ArrayDeque<>());
            stringQueue.add(msg);
            userMsg.put(u.userId, stringQueue);
        });
        this.groupMessage.put(group, userMsg);
        group.users.stream().filter(user -> user.isLogin && !user.userId.equals(msg.from))
                .forEach(user -> {
                    user.acceptMsg(msg);
                });
    }

    @Override
    public Map<Group, Map<String, Queue<Message>>> getGroupMessage() {
        return groupMessage;
    }

}

 

Colleague类

public abstract class Client {

    protected Mediator mediator;
    protected User user;
    protected String version;

    public Client(Mediator mediator, User user) {
        this.mediator = mediator;
        this.user = user;

    }

    public void login() {
        this.user.setLogin(true);
        log.info("用户:{} 登陆成功", user.userId);
        //用户上线后还有没有要接受的消息
        mediator.getGroupMessage().forEach(((group, msgs) -> {
            msgs.forEach((from, msg) -> {
                if (from.equals(user.userId)) {
                    while (!msg.isEmpty()) {
                        Message offineMsg = msg.poll();
                        log.info("用户:{} 接受到来自:{} 离线群组消息==>{},消息时间:{}", user.userId, offineMsg.from, offineMsg.getMsg(), offineMsg.getDateTime());

                    }
                }

            });

        }));

    }

    public void sendGroupMsg(String msg, Group group) {

        Message message = new Message();
        message.setMsg(msg + "客户端 " + this.getVersion());
        message.setFrom(this.user.userId);
        message.setDateTime(LocalDateTime.now());
        this.mediator.sendMsg(message, group);
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }
}

 

具体Colleague类

public class IOSClient extends Client {

    public IOSClient(Mediator mediator, User user) {
        super(mediator, user);
        setVersion("IOS16");
    }
    
}

public class AndroidClient extends Client {


    public AndroidClient(Mediator mediator, User user) {
        super(mediator, user);
        setVersion("Android12");
    }


}

 

聊天组

public class Group {

    Set<User> users = new HashSet<>();
    Set<User> loginUsers = new HashSet<>();
    String name;

    void joinGroup(User user) {
        users.add(user);
    }


    public Set<User> getUsers() {
        return users;
    }

    public Set<User> getLoginUsers() {
        return loginUsers;
    }
}

 

测试

 public static void main(String[] args) {
        Group group = new Group();
        User zhangsan = new User("张三");
        User lisi = new User("李四");
        User wangwu = new User("王五");
        group.joinGroup(zhangsan);
        group.joinGroup(lisi);
        group.joinGroup(wangwu);

        IMServerMediator imServer = new IMServerMediator();


        Client androd = new AndroidClient(imServer, lisi);
        androd.login();
        androd.sendGroupMsg("安卓端发送消", group);

        Client ios = new IOSClient(imServer, zhangsan);
        ios.login();
        ios.sendGroupMsg("IOS端发送消息", group);

        Client wangwuAndriod = new AndroidClient(imServer, wangwu);
        wangwuAndriod.login();
}

客户端发送消息,由中介者将消息发送给组类组内的客户端用户

 

优点

  1. 降低耦合度:中介者模式通过减少对象之间的直接通信,将对象解耦,使得修改一个对象不会影响到其他对象。
  2. 简化对象交互:中介者模式将对象之间的通信逻辑集中在一个中介者对象中,使得交互逻辑更加清晰和简单。
  3. 促进复用:中介者对象可用于多个对象之间的通信,提高了代码的复用性和可维护性。

缺点

  1. 单点故障:中介者对象成为了系统中的关键点,一旦中介者出现问题,可能影响整个系统的运作。
  2. 增加复杂性:当中介者模式被过度使用时,可能会导致中介者对象变得复杂,难以维护和理解。

中介者模式的应用

ExecutorService (zlennon.com)

ScheduledExecutorService (zlennon.com)

 

api gateway模式

定义

API Gateway 是一种设计模式或者服务,作为系统架构中的入口点,负责管理和处理所有进入系统的请求。它充当了整个系统的前端接口,提供了一个集中式的访问点,用于处理和转发来自客户端的请求到相应的后端服务或微服务。

维基百科

API网关是充当API前置,接收API请求,执行限制和安全策略,将请求传递到后端服务,然后将响应传递回请求者的服务器。网关通常包括一个转换引擎,以实时地编排和修改请求和响应。 网关可以提供收集分析数据和提供缓存等功能。网关还可以提供支持身份验证,授权,安全性,审计和法规遵从性的功能。

意图

  • API网关将所有对微服务的调用聚合到一起。用户对API网关进行一次调用,然后API网关调用每个相关的微服务。

参与者(角色)

  • 客户端:发起请求的外部用户或应用程序,通过 API Gateway 向系统发送请求。

  • API Gateway:充当系统的入口点,接收来自客户端的请求,并根据配置的规则和逻辑对请求进行处理和路由。

  • 后端服务(Microservices 或其他服务):实际执行业务逻辑的服务或微服务。API Gateway会根据路由规则将请求转发到这些服务。

举例

以获取订单和产品为例

类图:

ApiGateway类,请求的统一入口处

@RestController
public class ApiGateway {

  @Resource
  private OrderClient orderClient;

  @Resource
  private ProductClient productClient;

  @GetMapping("/getProduct/{id}")
  public Product getProduct(@PathVariable Long id) {

    return productClient.getProduct(id);
  }

  @GetMapping("/getOrder/{id}")
  public Order getOrder(@PathVariable Long id) {

    return orderClient.getOrder(id);
  }
}

 

OrderClient 类,调用订单服务

@Component
public class OrderClient {
    @Autowired
    RestTemplate restTemplate;

    public Order getOrder(Long id) {
        return restTemplate.getForObject("http://localhost:8001/orders/{id}", Order.class, id);

    }
}

 

ProductClient ,调用产品服务

@Component
public class ProductClient {

    @Autowired
    RestTemplate restTemplate;


    public Product getProduct(Long id) {
        return restTemplate.getForObject("http://localhost:8003/products/{id}", Product.class, id);
    }
}

 

测试

localhost:8002/getOrder/2

localhost:8002/getProduct/1

 

优点

  1. 统一入口点:提供了一个统一的入口点,简化了客户端访问多个服务的复杂性,降低了客户端和服务之间的耦合度。

  2. 路由和转发:能够基于请求的路由规则,将请求路由到正确的后端服务,实现了请求的转发和分发。

  3. 安全性控制:可以集中管理认证、授权和安全策略,确保请求的合法性和安全性,减少了安全风险。

  4. 负载均衡和缓存:支持负载均衡,将请求分发到多个服务实例,提高系统的性能和可用性。同时,对请求和响应进行缓存,减少重复计算和网络延迟。

  5. 协议转换和适配:能够处理不同协议之间的转换,使得客户端和服务之间可以使用不同的通信协议。

  6. 监控和管理:提供日志记录、监控和分析功能,帮助对系统进行管理、诊断和故障排查。

缺点

  1. 单点故障:API Gateway 作为整个系统的入口点,如果发生故障,可能导致整个系统不可用。

  2. 性能瓶颈:可能会成为系统的性能瓶颈,特别是在高负载情况下,处理大量请求可能会导致延迟增加。

  3. 复杂性增加:引入了额外的组件,增加了系统的复杂性和维护成本。需要考虑配置、路由规则、安全性等方面的管理。

  4. 过度集中化:过于集中的 API Gateway 可能使得系统过度依赖于单一组件,难以扩展和维护。

  5. 限制自由度:某些场景下,API Gateway 可能对系统的自由度和灵活性有所限制,特别是在不同团队开发的多个服务之间需要独立性的情况下。

 

Api- Gateway的应用

spring-cloud-gateway

nginx反向代理