Rust語言之GoF設計模式: 模板方法模式

banq 發表於 2022-09-26
設計模式 Go

模板方法是一種行為設計模式,它允許您在基類中定義演算法的骨架,並讓子類覆蓋這些步驟而不改變整體演算法的結構。
模板方法是定義演算法骨架的預設實現,其他方法可以在具體型別中重新定義。

這裡演算法整體結構不只是指普通演算法,可以指指具有凝聚性的業務邏輯聚合,在領域模型中定義業務邏輯的整體結構,不涉及實現細節,定義這些整體結構中元素之間的關係,類似橋模式中的一種組合方式,也類似組合模式中保護繁雜的樹形凝聚結構關係。

模板方法模式在 Python 框架中很常見。開發人員經常使用它為框架使用者提供一種使用繼承擴充套件標準功能的簡單方法。如果您在基類中看到一個方法呼叫了一堆其他抽象或空的方法,則可以識別模板方法。

Java中用抽象類實現模板。

Rust中用介面trait實現:

main.rs

trait TemplateMethod {
    fn template_method(&self) {
        self.base_operation1();
        self.required_operations1();
        self.base_operation2();
        self.hook1();
        self.required_operations2();
        self.base_operation3();
        self.hook2();
    }

    fn base_operation1(&self) {
        println!("TemplateMethod says: I am doing the bulk of the work");
    }

    fn base_operation2(&self) {
        println!("TemplateMethod says: But I let subclasses override some operations");
    }

    fn base_operation3(&self) {
        println!("TemplateMethod says: But I am doing the bulk of the work anyway");
    }

    fn hook1(&self) {}
    fn hook2(&self) {}

    fn required_operations1(&self);
    fn required_operations2(&self);
}

struct ConcreteStruct1;

impl TemplateMethod for ConcreteStruct1 {
    fn required_operations1(&self) {
        println!("ConcreteStruct1 says: Implemented Operation1")
    }

    fn required_operations2(&self) {
        println!("ConcreteStruct1 says: Implemented Operation2")
    }
}

struct ConcreteStruct2;

impl TemplateMethod for ConcreteStruct2 {
    fn required_operations1(&self) {
        println!("ConcreteStruct2 says: Implemented Operation1")
    }

    fn required_operations2(&self) {
        println!("ConcreteStruct2 says: Implemented Operation2")
    }
}

fn client_code(concrete: impl TemplateMethod) {
    concrete.template_method()
}

fn main() {
    println!("同一客戶程式碼可以與不同的具體實現一起工作:");
    client_code(ConcreteStruct1);
    println!();

    println!("同一客戶程式碼可以與不同的具體實現一起工作:");
    client_code(ConcreteStruct2);
}



流程模板
如果業務中步驟流程固定,而且這個流程必須固定才符合業務規則,那麼可以把流程用模板方法寫死:
基類定義一個工作流,派生類將覆蓋工作流中的方法。
首先我們定義一個BaseHandler trait來作為基類定義工作流程:然後我們的 "派生 "類UserHandler將透過重寫方法:

trait BaseHandler {
    fn authorized(&self) -> bool {
        false
    }

    fn get(&self) -> String {
        if !self.authorized() {
            return "403 Forbidden".to_string();
        }
        println!("Do some basic work");
        // handle rest work to do_get
        self.do_get()
    }

    fn do_get(&self) -> String;
}

struct UserHandler;

impl BaseHandler for UserHandler {
    fn authorized(&self) -> bool {
        true
    }

    fn do_get(&self) -> String {
        "Processed by User handler".to_string()
    }
}

fn main() {
    let handler = UserHandler {};
    println!("{}", handler.get());
}


輸出:

Do some basic work
Processed by User handler


開發框架與模板
Spring等Java框架是模板方法的升級,框架和模板化的好處是統一風格,提高團隊工作效率,下面是網友經驗:

其實我對Go很感興趣,因為它被設計成一種更有生產力的語言。然而,我注意到,由於沒有一個流行的框架,而大多數Go程式設計師不喜歡使用框架,這將導致一系列較小的設計決定。基本上,透過使用Spring,我得到了跨程式碼庫的統一性,這有助於我提高工作效率。在Go中,似乎要花更多的精力才能獲得這種效果。我沒有專業地使用Go,只是一些想法。