设计模式(一)设计原则

开闭原则

定义:一个软件实体,如类、模块、函数应该对扩展开放,对修改关闭。例如面向抽象编程,而不要面向实现编程。
优点:提高软件系统的可复用性、可维护性。
例如对于一个汽车类Car来说,有汽车品牌属性和汽车引擎,如果按照下面的设计:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Car {
    /**
     * 汽车型号名称
     */
    private String carName;
    /**
     * 汽车引擎
     */
    private V4Engine engine;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
class V4Engine{
    private String engineName;
}

@Slf4j
class Test{
    public static void main(String[] args) {
        Car car = new Car();
        car.setCarName("皮卡");
        car.setEngine(new V4Engine("V4发动机"));
        log.info("{}",car);
    }
}

如果要修改发动机,就不得不修改Car类。发动机的变化应该独立出来,不能修改原有类,但可以扩展原有类。如果要加新的发动机,不能修改原有的Car类,只需要添加新的发动机类就行了,如下所示:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Car {
    /**
     * 汽车型号名称
     */
    private String carName;
    /**
     * 汽车引擎
     */
    private Engine engine;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
abstract class Engine{
    protected String engineName;

}

class V4Engine extends Engine{
    V4Engine(String engineName){
        super(engineName);
    }
}

class V8Engine extends Engine{
    V8Engine(String engineName){
        super(engineName);
    }
}

@Slf4j
class Test{
    public static void main(String[] args) {
        Car car = new Car();
        car.setCarName("皮卡");
        car.setEngine(new V4Engine("V4发动机"));
        log.info("{}",car);

        car.setEngine(new V8Engine("V8发动机"));
        log.info("{}",car);
    }
}

在这里插入图片描述

依赖倒置原则

定义:高层不应该依赖底层模块,二者都应该依赖抽象。要针对接口编程,不要针对实现编程。

优点:减少类的耦合性,提高稳定性、代码可读性、可维护性。

依赖倒置原则和开闭原则举例:

MyBatis在使用Mapper的时候

SqlSession sqlSession = sqlSessionFactory.openSession();

会调用DefaultSqlSessionFactory的openSessionFromDataSource方法。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);}

其中Configuration.newExecutor方法。

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

Configuration持有的引用是Executor接口,而Executor接口有3个实现类:

在这里插入图片描述

BaseExecutor抽象类使用模板方法模式,定义了通用的方法实现。供ReuseExecutor、SimpleExecutor、BatchExecutor调用。
SimpleExecutor是最简单的Executor接口实现,ReuseExecutor和SimpleExecutor的区别是:ReuseExecutor能重用Statement。而BatchExecutor能够批量执行sql。
CachingExecutor使用了装饰模式,给Executor接口实现类封装了二级缓存的功能,稍后专写一篇关于它的介绍。

这里就遵循了依赖导致原则和开闭原则,Configuration持有的引用是Executor接口,依赖的是Executor接口,而非具体的实现类。同时对扩展开放,如果要增加新的Executor,只需要新增加一个XXXExecutor,并在初始化的时候new即可。

单一职责原则

定义:一个类、接口、方法应该只有一个职责。

现实编码中,很难严格遵守,基本上没有哪个类做到单一职责。建议方法级别做到单一职责原则。

优点:降低类的复杂度,提高类的可读性、可维护性。

单一职责举例:

在MyBatis的mapper文件中经常可以看到通用型的update方法:

public int update(Student student);

对应的xml文件往往是:

<update id="update">
    UPDATE student SET 
    name = #{name}, 
    age = #{age}, 
    gender = #{gender}, 
    phone = #{phone}, 
    class = #{class}, 
    type = #{type}, 
    update_by = #{updateBy.id}, 
    update_date = #{updateDate}, 
    remarks = #{remarks}
    WHERE id = #{id}
</update>

无论要修改Student的那个属性,所有属性都要修改一遍,如果要修改name,就要写一个单一职责的方法:

public int update(@Param("name") String name,@Param("id") String id);
<update id="update">
    UPDATE student SET 
    name = #{name}
    WHERE id = #{id}
</update>

迪米特法则

定义:又叫做最少知道原则。一个对象应该对其他对象保持最少的了解。

强调只和朋友交流,不和陌生人说话。

出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。

优点:降低耦合度。

迪米特法则举例:

Java中有PO/Entity: persistent object持久对象、VO:view object表现层对象、DTO:data transfer object,数据传输对象、DAO :data access object数据访问对象、POJO :plain ordinary java object用这个名字用来强调它是一个普通java对象,而不是一个特殊的对象。

它们有时可以通用,各自有各自的生命周期作用范围。

在这里插入图片描述

在这里插入图片描述

在实际的开发中,经常可以看到Entity从持久层一直传送到Controller层,违反了迪米特法则,Controller、Service、Dao层每一层应该仅和自己的“朋友”打交道。

接口隔离原则

定义:client不应该依赖它不需要的接口。接口依赖遵循最少依赖原则。尽量细化接口。

优点:使得类具有好的可读性、可扩展性、可维护性。

里氏替换原则

定义:如果将程序中的对象A替换为对象B,程序的行为没有发生变化,那么B就是A的子类型。

所有引用父类的地方必须能够透明的使用其字类的对象,字类对象能够替换父类对象, 同时程序逻辑不变。

子类可以扩展父类的功能,但不能改变父类原有功能。

子类可以实现父类的abstract方法,但不能覆盖父类的非abstract方法。子类可以增加自己特有的方法。

子类重载父类的方法时,方法的入参要比父类的方法的入参更宽松。方法的出参要比父类的方法出参更严格或一样。

优点:提高程序健壮性。提高可维护性、扩展性。

合成复用原则

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

总结

找出可变化之处,独立出来。
针对接口/抽象编程,而不是针对实现编程。
为了交互对象之间的松耦合而努力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值