欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

设计模式前言-设计原则、面向对象和Java语言特性

程序员文章站 2024-03-17 17:19:22
...

设计原则是心法,设计模式是招式。掌握心法,以不变应万变,无招胜有招。所以在学习设计模式之前,个人建议先了解本文提到的内容。

一、设计原则

提到设计原则,你能想起哪些设计原则?在编程的时候,你是否会思考这段代码有没有违背哪些原则?接下来,我们一起温习下六大设计原则:单一原则、开闭原则、里氏替换原则、迪米特法则(最少知道原则)、接口隔离原则、依赖倒置原则。

六大设计原则首字母合起来,称为SOLID(稳定的意思),其中里氏替换原则和迪米特法则共用一个L。

1.单一原则(SRP)

单一原则(Single Responsibility Principle)。它就是长辈们口中经常提到的“一个函数应该只做一件事”。

定义

A class or module should have a single reponsibility. 一个类或模块应该只完成一个职责(或功能)。

建议

切记不要过于一味的追求单一原则,还要考虑高内聚。

2.开闭原则(OCP)

开闭原则(Open Closed Principle)。工作中,是否经历过增加一个需求,结果导致原先正常的功能bug了。

定义

Software entities (modules, classes, functions, etc.) should be open for extension , but closed for modification. 软件实体(模块,类,函数等等)应该对扩展开放,但是对修改关闭。

建议

开闭原则并不是完全拒绝修改,而是以最小的修改代码的代价来完成新功能的开发。

3.里氏替换原则(LSP)

里氏替换原则(Liskov Substitution Principle)。工作中,在重写过父类函数时,是否在意重写的函数与父类的函数逻辑是否一致?

定义

Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing it。子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。

案例


public class Parent {

    private static final String DEFAULT_NAME = "defaultName";
    private static final String DEFAULT_CODE = "defaultCode";

    public void create(String name, String code){

        if(name == null && code == null){
            name = DEFAULT_NAME;
            code = DEFAULT_CODE;
        }

        // ......
    }
}

// 以下子类不符合里氏替换原则,因为逻辑被替换了。父类当参数为空给的是默认值,而子类则是直接抛出异常

public class SubClass extends Parent{

    @Override
    public void create(String name, String code) {
        if(name == null || code == null){
            throw new IllegalArgumentException("name或code不能为null");
        }
        super.create(name, code);
    }
}

4.迪米特法则(LOD)

迪米特法则(Law of Demeter)。也叫做最小知识原则(The Least Knowledge Principle)。

写出“高内聚,低耦合”的代码。

定义

Each unit should have only limited knowledge about other units: only units “closely” related to the current unit. Or: Each unit should only talk to its friends; Don’t talk to strangers. 每个模块只应该了解那些与它关系密切的模块的有限知识。或者说,每个模块只和自己的朋友“说话”,不和陌生人“说话”。

迪米特法则是希望减少类之间的耦合,让类越独立越好。也就是高内聚低耦合。

5.接口隔离原则(ISP)

接口隔离原则(Interface Segregation Principle)。

定义

Clients should not be forced to depend upon interfaces that they do not use. 客户端不应该被强迫依赖它不需要的接口。

案例

例如你需要提供一个用户查询的相关接口,不提供增删改的接口。你会如何处理?

// 查询接口
public interface UserService {
    
    String queryById(String id);
}

// 增删改接口
public interface UserManagerService extends UserService {

    void insert(String name);

    void delete(String id);

    void update(String id , String name);
    
}

@Component
public class UserServiceImpl implements UserManagerService {

    @Override
    public void insert(String name) {

    }

    @Override
    public void delete(String id) {

    }

    @Override
    public void update(String id, String name) {

    }

    @Override
    public String queryById(String id) {
        return null;
    }
}

// 可以选择注入

@Autowired
private UserService userService;
@Autowired
private UserManagerService userManagerService;

6.依赖反转原则(DIP)

依赖反转原则(Dependency Inversion Principle),也称为依赖倒置原则。

定义

High-level modules shouldn’t depend on low-level modules. Both modules should depend on abstractions. In addition, abstractions shouldn’t depend on details. Details depend on abstractions.

高层模块不要依赖低层模块。高层模块和低层模块应该通过抽象来互相依赖。除此之外,抽象不要依赖具体实现细节,具体实现细节依赖抽象。

案例:Servlet

Tomcat的Engine添加Servlet属性,Spring的DispatcherServlet实现了Servlet接口。两者都依赖于实现,而是依赖于接口。

二、面向对象

基本特征

封装(Encapsulation)

封装是为了提高代码可维护性和易用性,降低接口复杂度,来隐藏信息或者保护数据。

Java语言利用访问修饰符来达到封装的目的。private,protected,public三个级别,当不使用访问修饰符时,类默认是同一个包下可见,接口默认是public级别。

继承(Inheritance)

继承是为了代码复用。将公共部分提取到父类中,让不同的子类来继承父类。

Java语言支持单继承多实现。

为什么不支持多继承?设计者的初衷,让java更简单,所以选择了单继承多实现,避免多继承中函数冲突的问题。

1.8之后允许接口有default的方法,如果多接口中默认实现的函数定义一致,则会失去默认实现的意义,必须实现类自己来实现。


public interface InterA {

    default  void action(){
        System.out.println("InterA action");
    }
}

public interface InterB {

    default  void action(){
        System.out.println("InterB action");
    }
}

// 提示:Inter inherits unrelated defaults for action() from types InterA and InterB
public class Inter implements  InterA, InterB {
}

// 必须要实现action函数
public class Inter implements  InterA, InterB {
    @Override
    public void action() {
        new InterA(){}.action();
    }
}

多态(Polymorphism)

多态可以提高代码的扩展性和复用性,子类可以替换父类,在实际的代码运行过程中,调用子类的方法实现。

多态特性的实现方式,JAVA语言有“方法重写”和“接口”,还有duck-typing语法。

三、Java语言特性

该章节为java语言的特性,在接下来的设计模式中部分代码示例会使用到。

类的加载顺序

public class Parent {

    static{
        System.out.println("Parent static {} ");
    }

    public Parent(){
        System.out.println("Parent constructor ");
    }
}

public class Child extends Parent {

    static {
        System.out.println("Child static {} " + ChildSubClassA.INT);
    }

    {
        System.out.println("Child {} " + ChildSubClassA.INT);
    }

    public Child() {
        System.out.println("Child constructor ");
    }

    public static void main(String[] args) {
        new Child();
    }

    private static class ChildSubClassA {
        public static Child INT;

        static {
            System.out.println("ChildSubClassA static {} " + INT);
            INT = new Child();
        }
    }
}

// 打印如下

// Parent static {}
// ChildSubClassA static {} null
// Parent constructor
// Child {} null
// Child constructor
// Parent static {} xxxxx
// Parent constructor
// Child {} xxxxx
// Child constructor

设计模式前言-设计原则、面向对象和Java语言特性