設計模式前言-設計原則、面向對象和Java語言特性

設計原則是心法,設計模式是招式。掌握心法,以不變應萬變,無招勝有招。所以在學習設計模式之前,個人建議先了解本文提到的內容。

一、設計原則

提到設計原則,你能想起哪些設計原則?在編程的時候,你是否會思考這段代碼有沒有違背哪些原則?接下來,我們一起溫習下六大設計原則:單一原則、開閉原則、里氏替換原則、迪米特法則(最少知道原則)、接口隔離原則、依賴倒置原則。

六大設計原則首字母合起來,稱爲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

關注我

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章