我正在尝试实施策略设计模式,并且想知道我是否正确执行了该方法。
可以说,我有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);
}
}
答案 0 :(得分:1)
在策略的设计模式术语中,您的FormBuilder充当上下文的角色,该上下文持有对所使用的当前策略的引用(IFormStragegy
)。该策略是从外部(使用setter
)传递的,因此可以扩展(OCP)。因此,关于您的问题:
FormBuilder
内部选择策略,而不是从外部传递策略是否正确?这不是正确执行策略。您应该创建策略的实例并将其传递给上下文。因此,可以在运行时交换该策略。
是的,如果不进行更改,您将无法使FormBuilder知道新的策略。
您可以查看here作为示例。
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 */)
答案 1 :(得分:1)
您问了2个未直接链接到TypeScript的问题。该代码可以直接转换为常用的主流OOP语言C#/ Java。更加有趣,因为它涉及到设计模式和 SOLID 原理,这是面向对象编程的两个支柱。
在更笼统之前让我们具体回答一下:
- 在
FormBuilder
内部选择策略,而不是从外部传递策略是否正确?
是的。相反的情况导致FormFactory
包装器没有太大的兴趣。为什么不直接致电strategy.execute()
?
- 这不违反开放封闭原则吗?因此,如果我想再添加一种表单策略或删除现有的表单策略,则必须编辑
FormBuilder
类。
Builders 和 Factories 通过设计紧密地与下面创建的类型耦合。这是对OCP的局部违反,但是使用它们,客户端代码就与表单创建实现细节分离了。
其他评论
IFormStrategy
首先是一个(抽象的) Factory :它创建一个Form
。更好的名称应该是IFormFactory { create(...): Form; }
(或者仅仅是FormFactory
,“ I”前缀在C#中比在TypeScript中更常见)。这是FormBuilder
的策略,但不是本质上的。顺便说一句,在命名类时很少使用 Strategy 策略,因为它太通用了。最好使用更明确/明确的术语。formBuilder.withPartA().withPartB().build();
之类的流畅API。此类根据输入参数选择适当的 Factory / Strategy 。这是策略选择器或工厂工厂:D,它们也调用工厂以最终创建Form
。也许做的太多:仅选择工厂就足够了。也许合适,从客户端代码中隐藏了复杂性。interface
/ type
定义高阶函数,但没有包装对象/ class可能不会带来更多价值。客户端代码只需传递另一个函数即可,该函数可以是“简单lambda”(胖箭头函数)。params
参数由 Builder 和 Factories 使用。最好将其拆分,以避免混淆不同的关注点:策略选择和表单创建。createSimpleForm(simpleFormArgs)
,{ {1}} ...每个方法将实例化关联的工厂,并将其称为createExtendedForm(extendsFormArgs)
方法。这样,就不需要基于create(formArgs)
或if
的复杂算法来选择策略,而这会增加环复杂性。调用每个switch
方法也将更简单,而object参数则更少。 答案 2 :(得分:0)
策略是一种行为设计模式,它将一组行为转变为对象,并使它们在原始上下文对象内可互换。
原始对象(称为上下文)持有对策略对象的引用,并委托其执行行为。为了更改上下文执行其工作的方式,其他对象可以将当前链接的策略对象替换为另一个对象。
用法示例: 策略模式在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