[英]How to correctly implement strategy design pattern
我正在嘗試實施策略設計模式,並想知道我是否正確執行。
可以說,我有類FormBuilder
,它使用下面列表中的策略來構建表單:
SimpleFormStrategy
ExtendedFormStrategy
CustomFormStrategy
所以問題是:
FormBuilder
內部選擇策略而不是從外部傳遞策略是否正確?FormBuilder
類。草稿代碼示例
class Form {
// Form data here
}
interface IFormStrategy {
execute(params: object): Form;
}
class SimpleFormStrategy implements IFormStrategy {
public execute(params: object): Form {
// Here comes logics for building simple form
return new Form();
}
}
class ExtendedFormStrategy implements IFormStrategy {
public execute(params: object): Form {
// Here comes logics for building extended form
return new Form();
}
}
class CustomFormStrategy implements IFormStrategy {
public execute(params: object): Form {
// Here comes logics for building custom form
return new Form();
}
}
class FormBuilder {
public build(params: object): Form {
let strategy: IFormStrategy;
// Here comes strategy selection logics based on params
// If it should be simple form (based on params)
strategy = new SimpleFormStrategy();
// If it should be extended form (based on params)
strategy = new ExtendedFormStrategy();
// If it should be custom form (based on params)
strategy = new CustomFormStrategy();
return strategy.execute(params);
}
}
在 Strategy 的設計模式術語中,您的 FormBuilder 扮演 Context 的角色,它保存對當前使用的策略 ( IFormStragegy
) 的引用。 該策略從外部傳遞(使用setter
),因此它對擴展(OCP)開放。 所以關於你的問題:
FormBuilder
內部選擇策略而不是從外部傳遞策略是否正確?這不是戰略的正確實施。 您應該創建策略的實例並將其傳遞給上下文。 因此可以在運行時交換策略。
是的,您不能在不更改它的情況下使 FormBuilder 知道新策略。
您可以在此處查看示例。
FormBuilder context = new FormBuilder();
IFormStrategy simple = new SimpleFormStrategy();
IFormStrategy extended = new ExtendedFormStrategy();
IFormStrategy custom = new CustomFormStrategy();
context.setStrategy(simple);
context.build(/* parameters */)
context.setStrategy(custom);
context.build(/* parameters */)
您提出了 2 個與 TypeScript 沒有直接關聯的問題。 代碼可以直接轉換為C#/Java,通常的主流OOP語言。 它更有趣,因為它涉及設計模式和SOLID原則,這兩個都是面向對象編程的支柱。
在更籠統之前,讓我們具體回答一下:
- 在
FormBuilder
內部選擇策略而不是從外部傳遞策略是否正確?
是的。 相反會導致一個沒有太多興趣的FormFactory
包裝器。 為什么不直接調用strategy.execute()
?
- 這不違反開閉原則嗎? 因此,如果我想再添加一種表單策略或刪除現有的一種,我必須編輯
FormBuilder
類。
Builders和Factories通過設計與底層創建的類型緊密耦合。 這是對 OCP 的局部違反,但有了它們,客戶端代碼與表單創建實現細節分離。
雜項評論
IFormStrategy
首先是一個(抽象)工廠:它創建一個Form
。 更好的名字應該是IFormFactory { create(...): Form; }
IFormFactory { create(...): Form; }
(或只是FormFactory
,“我”的前綴是更多的C#常見比打字稿)。 這是FormBuilder
的一種策略,但不是本質上的。 順便說一下,在命名類時很少使用術語策略,因為它太通用了。 最好使用更具體/明確的術語。formBuilder.withPartA().withPartB().build();
這樣的流暢 API formBuilder.withPartA().withPartB().build();
. 此類根據輸入參數選擇適當的工廠/策略。 它是一個Strategy Selector或Factory of Factory :D ,它也調用工廠以最終創建Form
。 也許它做得太多了:只需選擇工廠就足夠了。 也許這是合適的,隱藏客戶端代碼的復雜性。interface
/ type
但沒有包裝對象/類,這可能不會帶來更多價值。 客戶端代碼只需要傳遞另一個可以是“簡單 lambda”(粗箭頭函數)的函數。params
參數由Builder和Factories 使用。 拆分它可能會更好,以避免混合不同的關注點:策略選擇和表單創建。createSimpleForm(simpleFormArgs)
, createExtendedForm(extendsFormArgs)
。 .. 每個方法都會實例化關聯的工廠並調用它create(formArgs)
方法。 這樣,就不需要復雜的算法來選擇策略,基於if
s 或switch
s 這會增加Cyclomatic Complexity 。 調用每個createXxxForm
方法也會更簡單,對象參數createXxxForm
。策略是一種行為設計模式,它將一組行為轉換為對象,並使它們在原始上下文對象中可互換。
原始對象,稱為上下文,持有對策略對象的引用,並委托它執行行為。 為了改變上下文執行其工作的方式,其他對象可以用另一個對象替換當前鏈接的策略對象。
使用示例:策略模式在 TypeScript 代碼中非常常見。 它經常用於各種框架中,為用戶提供一種無需擴展即可更改類行為的方法。
識別:策略模式可以通過讓嵌套對象完成實際工作的方法以及允許用不同的對象替換該對象的設置器來識別。
概念示例此示例說明了策略設計模式的結構。 它側重於回答以下問題: • 它由哪些類組成? • 這些類扮演什么角色? • 模式的元素以何種方式相關?
index.ts:概念示例
/**
* The Context defines the interface of interest to clients.
*/
class Context {
/**
* @type {Strategy} The Context maintains a reference to one of the Strategy
* objects. The Context does not know the concrete class of a strategy. It
* should work with all strategies via the Strategy interface.
*/
private strategy: Strategy;
/**
* Usually, the Context accepts a strategy through the constructor, but also
* provides a setter to change it at runtime.
*/
constructor(strategy: Strategy) {
this.strategy = strategy;
}
/**
* Usually, the Context allows replacing a Strategy object at runtime.
*/
public setStrategy(strategy: Strategy) {
this.strategy = strategy;
}
/**
* The Context delegates some work to the Strategy object instead of
* implementing multiple versions of the algorithm on its own.
*/
public doSomeBusinessLogic(): void {
// ...
console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)');
const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']);
console.log(result.join(','));
// ...
}
}
/**
* The Strategy interface declares operations common to all supported versions
* of some algorithm.
*
* The Context uses this interface to call the algorithm defined by Concrete
* Strategies.
*/
interface Strategy {
doAlgorithm(data: string[]): string[];
}
/**
* Concrete Strategies implement the algorithm while following the base Strategy
* interface. The interface makes them interchangeable in the Context.
*/
class ConcreteStrategyA implements Strategy {
public doAlgorithm(data: string[]): string[] {
return data.sort();
}
}
class ConcreteStrategyB implements Strategy {
public doAlgorithm(data: string[]): string[] {
return data.reverse();
}
}
/**
* The client code picks a concrete strategy and passes it to the context. The
* client should be aware of the differences between strategies in order to make
* the right choice.
*/
const context = new Context(new ConcreteStrategyA());
console.log('Client: Strategy is set to normal sorting.');
context.doSomeBusinessLogic();
console.log('');
console.log('Client: Strategy is set to reverse sorting.');
context.setStrategy(new ConcreteStrategyB());
context.doSomeBusinessLogic();
Output.txt:執行結果
Client: Strategy is set to normal sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e
Client: Strategy is set to reverse sorting.
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.