常用设计模式之白话精简理解及应用-下

工厂模式

上一篇我们先学习了单例和模板方法两个设计模式,单例模式在JDK中java.lang.Runtime使用饿汉式还有Spring从单例池获取bean的方法getSingleton,Spring解决循环依赖问题的核心也是采用三级缓存机制(三个Map存储Bean),接下来我们继续学习其他常见设计模式。

概述

  • 工厂模式将对象的创建和使用分开,即应用程序将对象的创建和初始化职责交给工厂对象;使用者不需要知道具体的创建过程,只需要使用即可。将实例化对象提取出来,放到一个类中统一维护和管理,从而达到解耦、提高项目的扩展性和维护性。
  • 降低代码重复,而且相比每次通过复杂的构造函数来创建初始化更容易使用。

代码示例

简单工厂

简单工厂不符合开闭原则,也不属于设计模式之一,但是在一些简单场景下还是比较实用的。

动物接口Animal.java

package cn.itxs.pattern.factory;

public interface Animal {
    void showAnimal();
}

羊Sheep.java

package cn.itxs.pattern.factory;

public class Sheep implements Animal{
    @Override
    public void showAnimal() {
        System.out.println("我是一只可爱的小绵羊!");
    }
}

鸭子Duck.java

package cn.itxs.pattern.factory;

public class Duck implements Animal{
    @Override
    public void showAnimal() {
        System.out.println("我是一只调皮的小鸭子!");
    }
}

动物生产工厂AnimalFactory.java,通过传入生产动物名返回对应动物对象

package cn.itxs.pattern.factory;

public class AnimalFactory {
    public Animal createAnimal(String name){
        Animal animal = null;
        if ( "鸭子".equals(name) ){
            animal =  new Duck();
        }else if ( "羊".equals(name) ){
            animal = new Sheep();
        }
        return animal;
    }
}

测试类

package cn.itxs.pattern.factory;

public class FactoryMain {
    public static void main(String[] args) {
        AnimalFactory animalFactory = new AnimalFactory();
        Animal animal = animalFactory.createAnimal("羊");
        animal.showAnimal();
    }
}

工厂方法

通过将动物生产工厂抽象为农场接口,以后扩充则只需要新增相应的工厂和动物类,不需要修改原有,符合开闭原则。

农场接口Farm.java

package cn.itxs.pattern.factory;

public interface Farm {
    Animal createAnimal();
}

生产鸭子农场DuckFarm.java

package cn.itxs.pattern.factory;

public class DuckFarm implements Farm{
    @Override
    public Animal createAnimal() {
        return new Duck();
    }
}

生产羊农场SheepFarm.java

package cn.itxs.pattern.factory;

public class SheepFarm implements Farm{
    @Override
    public Animal createAnimal() {
        return new Sheep();
    }
}

测试类

package cn.itxs.pattern.factory;

public class FactoryMain {
    public static void main(String[] args) {
        Farm farm = new DuckFarm();
        Animal animal= farm.createAnimal();
        animal.showAnimal();
    }
}

抽象工厂

抽象工厂也属于设计模式的一种,比如所有农场还要生产植物,且植物也有不同种类,比如农场即要生产羊还要生产番茄,抽象工厂就可以派上用场;而如果抽象工厂只有一个产品簇的维度则就退化为工厂方法。

植物接口Plant.java

package cn.itxs.pattern.factory;

public interface Plant {
    void showPlant();
}

玉米类Plant.java

package cn.itxs.pattern.factory;

public class Corn implements Plant{
    @Override
    public void showPlant() {
        System.out.println("我是一颗玉米!");
    }
}

番茄类Plant.java

package cn.itxs.pattern.factory;

public class Tomato implements Plant{
    @Override
    public void showPlant() {
        System.out.println("我是一颗番茄!");
    }
}

农场接口Farm.java

package cn.itxs.pattern.factory;

public interface Farm {
    Animal createAnimal();
    Plant createPlant();
}

深圳农场SZFarm.java生产鸭子和玉米

package cn.itxs.pattern.factory;

public class SZFarm implements Farm {
    @Override
    public Animal createAnimal() {
        return new Duck();
    }

    @Override
    public Plant createPlant() {
        return new Corn();
    }
}

广州农场GZFarm.java生产羊和番茄

package cn.itxs.pattern.factory;

public class GZFarm implements Farm {
    @Override
    public Animal createAnimal() {
        return new Sheep();
    }

    @Override
    public Plant createPlant() {
        return new Tomato();
    }
}

测试类

package cn.itxs.pattern.factory;

public class FactoryMain {
    public static void main(String[] args) {
        Farm farm = new GZFarm();
        Animal animal= farm.createAnimal();
        animal.showAnimal();
        Plant plant = farm.createPlant();
        plant.showPlant();
    }
}

建造者模式

概述

  • 建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
  • 建造者模式和工厂模式较相似,区别在于建造者模式是一种个性化产品的创建,而工厂模式则是一种标准化产品的创建;建造者模式注重的是方法顺序,工厂模式注重是创建对象;当创造一个对象需要很多步骤时 , 适合使用建造者模式 ;当创造一个对象只需要一个简单的方法就可以完成 , 适合使用工厂模式 。
  • 使用场景
    • 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员属性且有些是可选的参数。
    • 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
    • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。

代码示例

简单Builder

类似如Lombok的@Builder,JDK中的StringBuilder;如果创建的产品种类只有一种,只需要一个具体建造者,这时可以省略掉抽象建造者,甚至还可以省略掉指挥者角色

用户类User.java

package cn.itxs.pattern.builder;

public class User {
    private int id;
    private String name;
    private int age;
    private String homeAddress;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    public String getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(String homeAddress) {
        this.homeAddress = homeAddress;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", homeAddress='" + homeAddress + '\'' +
                '}';
    }
}

用户建造者类UserBuilder.java

package cn.itxs.pattern.builder;

public class UserBuilder {
    private User user = new User();

    public UserBuilder id(int id){
        user.setId(id);
        return this;
    }

    public UserBuilder name(String name){
        user.setName(name);
        return this;
    }

    public UserBuilder age(int age){
        user.setAge(age);
        return this;
    }

    public UserBuilder homeAddress(String homeAddress){
        user.setHomeAddress(homeAddress);
        return this;
    }

    public User build(){
        return user;
    }
}

测试类

package cn.itxs.pattern.builder;

public class BuilderMain {
    public static void main(String[] args) {
        User user = new UserBuilder().id(1).name("张三").age(25).homeAddress("海天一路").build();
        System.out.println(user);
    }
}

建造者模式和setter方法区别在于setter是先new出来对象,而建造者模式只会在最后build之后才能使用对象。

建造者

卧室类BedRoom.java

package cn.itxs.pattern.builder;

public class BedRoom {
    private String bed;
    private String light;
    private String dresser;

    public void setBed(String bed) {
        this.bed = bed;
    }

    public void setLight(String light) {
        this.light = light;
    }

    public void setDresser(String dresser) {
        this.dresser = dresser;
    }

    public void display(){
        System.out.println("BedRoom{" +
                "bed='" + bed + '\'' +
                ", light='" + light + '\'' +
                ", dresser='" + dresser + '\'' +
                '}');
    }
}

抽象装修工类Decorator.java

package cn.itxs.pattern.builder;

public abstract class Decorator {
    protected BedRoom bedRoom = new BedRoom();

    abstract void buildBed();
    abstract void buildLight();
    abstract void buildDresser();

    public BedRoom builder(){
        return bedRoom;
    }
}

豪华装修工人LuxuryDecorator.java

package cn.itxs.pattern.builder;

public class LuxuryDecorator extends Decorator{
    @Override
    void buildBed() {
        bedRoom.setBed("豪华大床");
    }

    @Override
    void buildLight() {
        bedRoom.setLight("豪华吊灯");
    }

    @Override
    void buildDresser() {
        bedRoom.setDresser("豪华梳妆台");
    }
}

简单轻装修工LightDecorator.java

package cn.itxs.pattern.builder;

public class LightDecorator extends Decorator{
    @Override
    void buildBed() {
        bedRoom.setBed("普通床");
    }

    @Override
    void buildLight() {
        bedRoom.setLight("普通照明灯");
    }

    @Override
    void buildDresser() {
        bedRoom.setDresser("普通梳妆台");
    }
}

装修指挥者DesignerManager.java

package cn.itxs.pattern.builder;

public class DesignerManager {
    private Decorator decorator;

    public DesignerManager(Decorator decorator) {
        this.decorator = decorator;
    }

    public BedRoom decorate(){
        decorator.buildBed();
        decorator.buildLight();
        decorator.buildDresser();
        return decorator.builder();
    }
}

测试类

package cn.itxs.pattern.builder;

public class BuilderMain {
    public static void main(String[] args) {
        Decorator decorator = new LuxuryDecorator();
        DesignerManager designerManager = new DesignerManager(decorator);
        BedRoom bedRoom = designerManager.decorate();
        bedRoom.display();
    }
}

策略模式

概述

  • 策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户;将变化的部分抽离出来,组合进类中,根据不同的子类,可以设置为不同的行为子类实现动态改变行为的目的。
  • 常见实现方式有两种
    • 自己决定策略(具体策略角色)、交给执行人(环境角色)去执行(执行人不去决定执行策略)比如计算器加减乘除策略
    • 自己不懂策略(具体策略角色)、交给执行人(环境角色)去执行(执行人懂得决定策略) -比如本篇的出行方式选择
  • 使用场景
    • 一个系统需要动态地在几种算法中选择一种时,可将每个算法封装到策略类中。
    • 一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现,可将每个条件分支移入它们各自的策略类中以代替这些条件语句。
    • 系统中各算法彼此完全独立,且要求对客户隐藏具体算法的实现细节时。
    • 系统要求使用算法的客户不应该知道其操作的数据时,可使用策略模式来隐藏与算法相关的数据结构。
    • 多个类只区别在表现行为不同,可以使用策略模式,在运行时动态选择具体要执行的行为。

代码示例

旅行方式接口TravelStrategy.java

package cn.itxs.pattern.strategy;

public interface TravelStrategy {
    void tripMode();
    boolean isFeasible(int mileage);
}

公交车出行BusStrategy.java

package cn.itxs.pattern.strategy;

public class BusStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用公交车出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage < 30){
            return true;
        }
        return false;
    }
}

汽车出行CarStrategy.java

package cn.itxs.pattern.strategy;

public class CarStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用汽车出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage>= 30 && mileage < 500){
            return true;
        }
        return false;
    }
}

高铁出行TrainStrategy.java

package cn.itxs.pattern.strategy;

public class TrainStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用高铁出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage >= 500 && mileage < 1000){
            return true;
        }
        return false;
    }
}

飞机出行AirPlaneStrategy.java

package cn.itxs.pattern.strategy;

public class AirPlaneStrategy implements TravelStrategy{
    @Override
    public void tripMode() {
        System.out.println("使用飞机出行");
    }

    @Override
    public boolean isFeasible(int mileage) {
        if (mileage > 1000){
            return true;
        }
        return false;
    }
}

出行决策环境类BusStrategy.java,由出行方式环境类来决策出行方式

package cn.itxs.pattern.strategy;

import java.util.ArrayList;
import java.util.List;

public class TravelContext {
    private List<TravelStrategy> travelStrategies;

    public TravelContext() {
        this.travelStrategies = new ArrayList<>();
        travelStrategies.add(new BusStrategy());
        travelStrategies.add(new CarStrategy());
        travelStrategies.add(new TrainStrategy());
        travelStrategies.add(new AirPlaneStrategy());
    }

    public void trip(int mileage){
        for (TravelStrategy travelStrategy : travelStrategies) {
            if (travelStrategy.isFeasible(mileage)){
                travelStrategy.tripMode();
                break;
            }
        }
    }
}

测试类

package cn.itxs.pattern.strategy;

public class StrategyMain {
    public static void main(String[] args) {
        TravelContext travelContext = new TravelContext();
        travelContext.trip(800);
    }
}

状态模式

概述

  • 状态模式允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
  • 状态模式可以有效的替换充满在程序中的if else语句:将不同条件下的行为封装在一个类里面,再给这些类一个统一的父类来约束他们。
  • 状态模式策略模式很相似,也是将类的"状态"封装了起来,在执行动作时进行自动的转换,从而实现,类在不同状态下的同一动作显示出不同结果;使用的目的是不同的——策略模式用来处理算法变化,而状态模式则是处理状态变化;策略模式中,算法是否变化完全是由客户程序决定的,而且往往一次只能选择一种算法,不存在算法中途发生变化的情况。
  • 使用场景
    • 一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。
    • 代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的分支语句 (if-else / switch-case) , 且这些分支依赖于该对象的状态。

代码示例

公司绩效考核结果状态,绩效考核状态抽象类PerformanceState.java

package cn.itxs.pattern.state;

abstract class PerformanceState {
    protected PerformanceContext pc;
    protected String stateName;
    protected int score;

    public abstract void checkState();

    public void addScore(int score){
        this.score += score;
        System.out.println("当前分数为:" + this.score);
        checkState();
        System.out.println("当前状态:"+pc.getPerformanceState().stateName);
    }
}

绩效考核环境类PerformanceContext.java

package cn.itxs.pattern.state;

public class PerformanceContext {
    private PerformanceState performanceState;

    public PerformanceContext() {
        performanceState = new DisqualificationState(this);
    }

    public PerformanceState getPerformanceState() {
        return performanceState;
    }

    public void setPerformanceState(PerformanceState performanceState) {
        this.performanceState = performanceState;
    }

    public void add(int score){
        this.performanceState.addScore(score);
    }
}

绩效不合格状态DisqualificationState.java

package cn.itxs.pattern.state;

public class DisqualificationState extends PerformanceState{

    public DisqualificationState(PerformanceContext performanceContext) {
        pc = performanceContext;
        stateName = "不合格";
        score = 0;
    }

    public DisqualificationState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "不合格";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score >= 60 && score<70){
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 80 && score<90){
            pc.setPerformanceState(new GoodState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

绩效需改进状态ImproveState.java

package cn.itxs.pattern.state;

public class ImproveState extends PerformanceState{

    public ImproveState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "需改进";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 80 && score<90){
            pc.setPerformanceState(new GoodState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

绩效合格状态QualificationState.java

package cn.itxs.pattern.state;

public class QualificationState extends PerformanceState{

    public QualificationState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "合格";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }if (score >= 60 && score<70) {
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 80 && score<90){
            pc.setPerformanceState(new GoodState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

绩效优良状态GoodState.java

package cn.itxs.pattern.state;

public class GoodState extends PerformanceState{

    public GoodState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "优良";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }if (score >= 60 && score<70) {
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 90){
            pc.setPerformanceState(new ExcellentState(this));
        }
    }
}

绩效优秀状态PerformanceState.java

package cn.itxs.pattern.state;

public class ExcellentState extends PerformanceState{

    public ExcellentState(PerformanceState performanceState) {
        pc = performanceState.pc;
        stateName = "优秀";
        score = performanceState.score;
    }

    @Override
    public void checkState() {
        if (score < 60){
            pc.setPerformanceState(new DisqualificationState(this));
        }if (score >= 60 && score<70) {
            pc.setPerformanceState(new ImproveState(this));
        }else if (score >= 70 && score<80){
            pc.setPerformanceState(new QualificationState(this));
        }else if (score >= 80 && score<90) {
            pc.setPerformanceState(new GoodState(this));
        }
    }
}

测试类,绩效考核随着分数变化状态相应变化

package cn.itxs.pattern.state;

public class StateMain {
    public static void main(String[] args) {
        PerformanceContext performanceContext = new PerformanceContext();
        performanceContext.add(50);
        performanceContext.add(10);
        performanceContext.add(11);
        performanceContext.add(12);
        performanceContext.add(13);
        performanceContext.add(-20);
    }
}

适配器模式

概述

  • 适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主要目的是兼容性,让原本因接口不匹配不能工作的两个类可以协同工作。
  • 类配置器模式属于静态的,通过继承实现,较少使用;对象配置器模式通过聚合对象持有他的实例,使用关联关系来替代继承关系,比较灵活,较常使用。
  • 使用场景
    • 系统需要使用现有的类,但现有的类却不兼容。
    • 需要建立一个可以重复使用的类,用于一些彼此关系不大的类,并易于扩展,以便于面对将来会出现的类。
    • 需要一个统一的输出接口,但是输入类型却不可预知。

代码示例

发动机接口Engine.java

package cn.itxs.pattern.adaptor;

public interface Engine {
    void drive();
}

电能驱动发动机类ElectricEngine.java

package cn.itxs.pattern.adaptor;

public class ElectricEngine {
    public void electricDrive(){
        System.out.printf("使用电能驱动发动机");
    }
}

太阳能驱动发动机类SolarEngine.java

package cn.itxs.pattern.adaptor;

public class SolarEngine {
    public void solarDrive(){
        System.out.printf("使用太阳能驱动发动机");
    }
}

电能适配器类ElectricEngine.java

package cn.itxs.pattern.adaptor;

public class ElectricAdaptor implements Engine{
    private ElectricEngine electricEngine;

    public ElectricAdaptor() {
        electricEngine = new ElectricEngine();
    }

    @Override
    public void drive() {
        electricEngine.electricDrive();
    }
}

太阳能适配器类ElectricEngine.java

package cn.itxs.pattern.adaptor;

public class SolarAdaptor implements Engine{
    private SolarEngine solarEngine;

    public SolarAdaptor() {
        solarEngine = new SolarEngine();
    }

    @Override
    public void drive() {
        solarEngine.solarDrive();
    }
}

测试类,实现电能适配

package cn.itxs.pattern.adaptor;

public class AdaptorMain {
    public static void main(String[] args) {
        Engine engine = new ElectricAdaptor();
        engine.drive();
    }
}

外观模式

概述

  • 外观模式要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行,门面模式提供一个高层的接口,使得子系统更易于使用;为复杂子系统提供一个更高层次的统一接口,只能通过该接口访问子系统。
  • 外观模式是较常使用的结构型设计模式,它通过引入一个外观角色来简化客户端与子系统之间的交互,为复杂的子系统调用提供一个统一的入口,降低子系统与客户端的耦合程度,且客户端调用非常方便。
  • 使用场景
    • 为一个复杂的模块或子系统提供一个供外界访问的接口;设计初期极端,应该有意识的将不同层分离,层与层之间建立外观模式。
    • 开放阶段,子系统越来越复杂,增加外观模式提供一个简单的调用接口。
    • 当一个系统的功能越来越复杂,依赖的子系统越来越多,客户对系统的访问也变得越来越复杂。这个时候如果系统内部发生改变,客户端也会跟着变化,所以就可以建立一个统一的接口来维护这些负责系统,在新系统需要与这些子系统交互的时候,直接调用该接口,方便接入和维护。

代码示例

用户子系统加积分User.java

package cn.itxs.pattern.facade;

public class User {
    public void credits(){
        System.out.println("加积分");
    }
}

商品子系统减库存Good.java

package cn.itxs.pattern.facade;

public class Good {
    public void deduct(){
        System.out.println("减库存");
    }
}

物流配送子系统下物流单Delivery.java

package cn.itxs.pattern.facade;

public class Delivery {
    public void send(){
        System.out.println("下物流单");
    }
}

订单门面提供下单请求统一处理OrderFacade.java

package cn.itxs.pattern.facade;

public class OrderFacade {
    private Good good = new Good();
    private User user = new User();
    private Delivery delivery = new Delivery();

    public void orderRequest(){
        good.deduct();
        user.credits();
        delivery.send();
    }
}

测试类

package cn.itxs.pattern.facade;

public class FacadeMain {
    public static void main(String[] args) {
        OrderFacade orderFacade = new OrderFacade();
        orderFacade.orderRequest();
    }
}

代理模式

概述

  • 代理是在不修改源代码的情况下使得原来不具备某种行为能力的类、对象具有该种行为能力,实现对目标对象的功能扩展或者增强。
  • 代理模式为其他对象提供一种代理以控制对这个对象的访问;由于某些原因需要给某对象提供一个代理以控制对该对象的访问时,如果访问对象不适合或者不能直接引用目标对象,那么代理对象就作为访问对象和目标对象之间的中介。
  • 代理一般分为静态代理和动态代理。
    • 静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。事先知道要代理的是什么,只能代理固定单一的接口,不能代理任意接口任意方法。
    • 动态代理类:在程序运行时,运用反射机制动态创建而成。
      • JDK动态代理:实现InvocationHandler接口的invoke方法,代理的是接口,也即是业务类必须要实现接口,通过Proxy里的newProxyInstance得到代理对象;也即是动态生成代理类的字节码放在内存中。
      • CGLIB动态代理,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行时,动态修改字节码达到修改类的目的。
  • 使用场景
    • 事务处理
    • 权限管理
    • 日志收集

代码示例

静态代理

商品操作数据库类GoodsMapper.java

package cn.itxs.pattern.proxy;

public class GoodsMapper {
    public void handleJDBC(){
        System.out.println("商品数据写入数据库");
    }
}

商品代理类GoodsStaticProxy.java

package cn.itxs.pattern.proxy;

public class GoodsStaticProxy {
    private GoodsMapper goodsMapper;

    public GoodsStaticProxy(GoodsMapper goodsMapper) {
        this.goodsMapper = goodsMapper;
    }

    public void handleJDBC(){
        System.out.println("获取数据库连接");
        goodsMapper.handleJDBC();
        System.out.println("关闭数据库连接");
    }
}

测试类,这样获取数据库和关闭数据库就由代理类来完成,

package cn.itxs.pattern.proxy;

public class StaticProxyMain {
    public static void main(String[] args) {
        GoodsStaticProxy goodsStaticProxy = new GoodsStaticProxy(new GoodsMapper());
        goodsStaticProxy.handleJDBC();
    }
}

如果有多个业务数据库操作类比如增加物流配送Mapper,先定义接口CommonMapper.java

package cn.itxs.pattern.proxy;

public interface CommonMapper {
    void handleJDBC();
}

商品操作数据库类GoodsMapper.java实现CommonMapper接口

package cn.itxs.pattern.proxy;

public class GoodsMapper implements CommonMapper{
    @Override
    public void handleJDBC(){
        System.out.println("商品数据写入数据库");
    }
}

物流配送操作数据库类GoodsMapper.java实现CommonMapper接口

package cn.itxs.pattern.proxy;

public class DeliveryMapper implements CommonMapper{
    @Override
    public void handleJDBC(){
        System.out.println("物流单数据写入数据库");
    }
}

通用代理类CommonStaticProxy.java

package cn.itxs.pattern.proxy;

public class CommonStaticProxy {
    private CommonMapper commonMapper;

    public CommonStaticProxy(CommonMapper commonMapper) {
        this.commonMapper = commonMapper;
    }

    public void handleJDBC(){
        System.out.println("获取数据库连接");
        commonMapper.handleJDBC();
        System.out.println("关闭数据库连接");
    }
}

测试类

package cn.itxs.pattern.proxy;

public class StaticProxyMain {
    public static void main(String[] args) {
        CommonStaticProxy commonStaticProxy = new CommonStaticProxy(new GoodsMapper());
        commonStaticProxy.handleJDBC();
        commonStaticProxy = new CommonStaticProxy(new DeliveryMapper());
        commonStaticProxy.handleJDBC();
    }
}

JDK动态代理

很明显如果还有其他接口需要统一代理则上面的静态代理则需要新增加代理类来实现,无法统一代理统一方法实现。增加行为接口BehaviorMapper.java

package cn.itxs.pattern.proxy;

public interface BehaviorMapper {
    void doLog();
}

登录日志类LoginMapper.java

package cn.itxs.pattern.proxy;

public class LoginMapper implements BehaviorMapper{
    @Override
    public void doLog() {
        System.out.println("登录日志业务数据库入库");
    }
}

ItxsInvocationHandle.java

package cn.itxs.pattern.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ItxsInvocationHandle implements InvocationHandler {

    private Object target;

    public ItxsInvocationHandle(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("获取数据库连接");
        method.invoke(target,args);
        System.out.println("关闭数据库连接");
        return null;
    }
}

测试类,可以实现CommonMapper接口和BehaviorMapper接口代理。

package cn.itxs.pattern.proxy;

import java.lang.reflect.Proxy;

public class DynamicProxyMain {
    public static void main(String[] args) {
        GoodsMapper goodsMapper = new GoodsMapper();
        ItxsInvocationHandle invocationHandle = new ItxsInvocationHandle(goodsMapper);
        CommonMapper goodsMapperI = (CommonMapper)Proxy.newProxyInstance(goodsMapper.getClass().getClassLoader(),goodsMapper.getClass().getInterfaces(),invocationHandle);
        goodsMapperI.handleJDBC();

        LoginMapper loginMapper = new LoginMapper();
        invocationHandle = new ItxsInvocationHandle(loginMapper);
        BehaviorMapper loginMapperI = (BehaviorMapper)Proxy.newProxyInstance(loginMapper.getClass().getClassLoader(),loginMapper.getClass().getInterfaces(),invocationHandle);
        loginMapperI.doLog();
    }
}

JDK动态代码源码如下,可以看出代理类名称\(Proxy后面加自增序列号,将生成类的字节码直接放在jvm的内存中,如果有兴趣也可以直接将将\)Proxy0的byte[] proxyClassFile的字节码写到本地文件看下类的信息。

image-20220112162146049

组合模式

概述

  • 组合模式是部分整体模式,属于结构型模式,它创建了对象组的树形结构,将对象组合成树状结构以表示“整体-部分”的层次关系。
  • 组合模式使得用户对单个对象和组合对象的访问具有一致性,即:组合能让客户以一致的方式处理个别对象以及组合对象。
  • 当发现需求中是用树形结构体现部分与整体层次关系的结构时,且用户可以忽略整体和部分、组合对象和单个对象的不同,统一地使用组合结构中的所有对象时,就应该使用组合模式。
  • 使用场景
    • 需要遍历组织机构,或者处理的对象具有树形结构时, 非常适合使用组合模式
    • 树形菜单
    • 文件、文件夹的管理

代码示例

商品接口Item.java

package cn.itxs.pattern.composite;

public interface Item {
    float calculate();
    void dispaly();
}

物品类实现商品接口Articles.java

package cn.itxs.pattern.composite;

public class Articles implements Item{

    private String name;
    private int quantify;
    private float price;

    public Articles(String name, int quantify, float price) {
        this.name = name;
        this.quantify = quantify;
        this.price = price;
    }

    @Override
    public float calculate() {
        return quantify*price;
    }

    @Override
    public void dispaly() {
        System.out.println("商品{" +
                "名称='" + name + '\'' +
                ", 数量=" + quantify +
                ", 单价=" + price +
                '}');
    }
}

袋子类实现商品接口Bag.java

package cn.itxs.pattern.composite;

import java.util.ArrayList;
import java.util.List;

public class Bag implements Item{
    private String name;
    private List<Item> bags = new ArrayList<Item>();

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

    public void add(Item item){
        bags.add(item);
    }

    public void remove(Item item){
        bags.remove(item);
    }

    public Item getChildNode(int index){
        return bags.get(index);
    }

    @Override
    public float calculate() {
        float total = 0;
        for (Item bag : bags) {
            total += bag.calculate();
        }
        return total;
    }

    @Override
    public void dispaly() {
        for (Item bag : bags) {
            bag.dispaly();
        }
    }
}

测试类,袋子可以存放商品或袋子,最终显示商品列表详情和总价

package cn.itxs.pattern.composite;

public class CompositeMain {
    public static void main(String[] args) {
        Bag bigBag = new Bag("大号袋子");
        Bag mediumBag = new Bag("中号袋子");
        Bag smallLucencyBag = new Bag("小号透明袋子");
        Bag smallGrayBag = new Bag("小号灰色袋子");

        Articles articles;
        articles = new Articles("金鲳鱼",1,25.6f);
        smallLucencyBag.add(articles);
        articles = new Articles("火龙果",5,4.9f);
        smallGrayBag.add(articles);
        articles = new Articles("柠檬",3,1.2f);
        smallGrayBag.add(articles);

        articles = new Articles("雪花啤酒",4,4.6f);
        mediumBag.add(articles);
        articles = new Articles("椰奶",2,4.5f);
        mediumBag.add(articles);
        mediumBag.add(smallLucencyBag);

        articles = new Articles("耐克运动鞋",1,299.9f);
        bigBag.add(articles);
        bigBag.add(smallGrayBag);
        bigBag.add(mediumBag);

        bigBag.dispaly();
        float total = bigBag.calculate();
        System.out.println("总计:" + total +"元");
    }
}

装饰者模式

概述

  • 装饰者模式在保持相同接口的同时,动态地给一个对象添加额外的功能。相对于继承的功能扩展而言,装饰器也是一个灵活的替代方式。
  • 装饰器模式最本质的特征是将原有类的附加功能抽离出来,简化原有类的逻辑。创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
  • 使用场景
    • 扩展一个类的功能。
    • 动态增加功能,动态撤销。

代码示例

人物接口Figure.java

package cn.itxs.pattern.decorate;

public interface Figure {
    void show();
}

绿箭侠原身类实现人物接口Arrow.java

package cn.itxs.pattern.decorate;

public class Arrow implements Figure{

    @Override
    public void show() {
        System.out.println("绿箭侠原身");
    }
}

变身父类或者装饰类实现人物接口Transfiguration.java

package cn.itxs.pattern.decorate;

public class Transfiguration implements Figure{
    Figure figure;

    public Transfiguration(Figure figure) {
        this.figure = figure;
    }

    @Override
    public void show() {
        figure.show();
    }
}

天使变身具体类Angel.java

package cn.itxs.pattern.decorate;

public class Angel extends Transfiguration{
    public Angel(Figure figure) {
        super(figure);
    }

    @Override
    public void show() {
        System.out.println("天使化身形象附在");
        super.show();
    }
}

恶魔变身具体类Figure.java

package cn.itxs.pattern.decorate;

public class Demon extends Transfiguration{
    public Demon(Figure figure) {
        super(figure);
    }

    @Override
    public void show() {
        System.out.println("恶魔化身形象附在");
        super.show();
    }
}

测试类

package cn.itxs.pattern.decorate;

public class DecorateMain {
    public static void main(String[] args) {
        Figure arrow = new Arrow();
        arrow.show();

        Figure arrowAngel = new Angel(arrow);
        arrowAngel.show();

        Figure arrowDemon = new Demon(arrow);
        arrowDemon.show();
    }
}

原型模式

概述

  • 原型模式的核心思想是,通过拷贝指定的原型实例(对象),创建跟该对象一样的新对象。简单理解就是克隆指定对象,属于创建型设计模式中的一种。
  • 需要拷贝的原型类必须实现"java.lang.Cloneable"接口,然后重写Object类中的clone方法,从而才可以实现类的拷贝。当一个类实现了Cloneable接口后,该类才会被赋予调用重写自Object类的clone方法得权利。否则会抛出“CloneNotSupportedException”异常。
  • 原型模式中的拷贝可分为浅拷贝和深拷贝。
    • 浅拷贝:当类的成员变量是基本数据类型时,浅拷贝会复制该属性的值赋值给新对象;当成员变量是引用数据类型时,浅拷贝复制的是引用数据类型的地址值。这种情况下,当拷贝出的某一个类修改了引用数据类型的成员变量后,会导致所有拷贝出的类都发生改变。
    • 深拷贝:深拷贝不仅会复制成员变量为基本数据类型的值,给新对象。还会给是引用数据类型的成员变量申请储存空间,并复制引用数据类型成员变量的对象。这样拷贝出的新对象就不怕修改了是引用数据类型的成员变量后,对其它拷贝出的对象造成影响了。有以下两种方式实现:
      • 将所有引用类型都执行clone复制。
      • 通过序列化方式。
  • 使用场景
    • 当系统中需要大量创建相同或者相似的对象时,就可以通过“原型设计模式”来实现。
    • 如果一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,那么可以使用原型模式。
    • 当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。

代码示例

浅克隆

证书类Certificate.java实现Cloneable接口

package cn.itxs.pattern.prototype;

public class Certificate implements Cloneable{
    private String name;
    private String company;
    private String detail;

    public Certificate(String name, String company, String detail) {
        this.name = name;
        this.company = company;
        this.detail = detail;
        System.out.println("证书创建成功");
    }

    public String getName() {
        return name;
    }

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

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public void show() {
        System.out.println("Certificate{" +
                "name='" + name + '\'' +
                ", company='" + company + '\'' +
                ", detail='" + detail + '\'' +
                '}');
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("证书拷贝成功");
        return (Certificate)super.clone();
    }
}

测试类

package cn.itxs.pattern.prototype;

public class ProtoTypeMain {
    public static void main(String[] args) throws CloneNotSupportedException {
        Certificate certificate = new Certificate("林有力","大疆无边科技股份有限公司","2021年获得信息系统架构师");
        certificate.show();
        Certificate clone = (Certificate)certificate.clone();
        clone.setName("李天生");
        clone.show();
    }
}

深克隆

如果上面证书Certificate类中有非基本类型的话,那么浅克隆无法对引用类型实现克隆。

等级类Level.java

package cn.itxs.pattern.prototype;

public class Level implements Cloneable{
    private String name;
    private String detail;

    public Level(String name, String detail) {
        this.name = name;
        this.detail = detail;
    }

    public String getName() {
        return name;
    }

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

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

证书类Certificate.java,还是使用上面克隆实现方式

package cn.itxs.pattern.prototype;

public class Certificate implements Cloneable{
    private String name;
    private String company;
    private String detail;
    private Level level;

    public Certificate(String name, String company, String detail, Level level) {
        this.name = name;
        this.company = company;
        this.detail = detail;
        this.level = level;
        System.out.println("证书创建成功");
    }

    public String getName() {
        return name;
    }

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

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public Level getLevel() {
        return level;
    }

    public void setLevel(Level level) {
        this.level = level;
    }

    public void show() {
        System.out.println("Certificate{" +
                "name='" + name + '\'' +
                ", company='" + company + '\'' +
                ", detail='" + detail + '\'' +
                ", level name='" + level.getName() + '\'' +
                ", level detail='" + level.getDetail() + '\'' +
                '}');
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("证书拷贝成功");
        return (Certificate)super.clone();
    }
}

测试类

package cn.itxs.pattern.prototype;

public class ProtoTypeMain {
    public static void main(String[] args) throws CloneNotSupportedException {
        Level level = new Level("高级", "中国软件最有影响力");
        Certificate certificate = new Certificate("林有力","大疆无边科技股份有限公司","2021年获得信息系统架构师",level);
        certificate.show();
        Certificate clone = (Certificate)certificate.clone();
        clone.setName("李天生");
        clone.show();
        level.setName("中级");
        clone.show();
    }
}

image-20220112191019841

再将Certificate.java中的clone()方法修改如下

    @Override
    public Object clone() throws CloneNotSupportedException {
        System.out.println("证书拷贝成功");
        Certificate certificate = (Certificate)super.clone();
        certificate.level = (Level) level.clone();
        return certificate;
    }

重新运行测试类,结果如下

image-20220112191805176

针对常用设计模式学习分享就暂时告一段落,后续有时间再补充其他的设计模式.........

posted @ 2022-01-12 23:36  itxiaoshen  阅读(320)  评论(0编辑  收藏  举报