如何在不违反LSP的情况下遵循打开-关闭原则,同时决定在静态类型语言中使用不同参数调用哪个方法?
考虑类似的要求
-
操作1:对表1 执行DB操作
-
动作2:根据输入对表2执行DB操作
-
行动3:什么都不做
上述要求的代码看起来像
process(obj) {
if(obj.type === action1) {
db.updateTable1()
}
if(obj.type === action2) {
db.updateTable2(obj.status)
}
if(obj.type === action3) {
//May be log action 3 recieved
}
}
在上面的代码中,通过将if语句的主体移动到方法并维护一个以action为名称的键映射,找到了一种遵循OCP的方法来执行其他操作。参考
然而,感觉解决方案违反了OCP,因为包装第一个if块的内容的方法将不接收任何参数,包装第二个if块将具有参数的内容的第二个方法。
要么强制所有方法在遵循OCP但违反LSP的权衡中遵循相同的签名,要么放弃OCP本身,从而使用multi-if语句。
一个简单的解决方案是定义一个策略,执行当前包含在if / else if / else
分支中的代码:
interface Strategy {
String getType();
void apply();
}
需要注册的策略:
class Executor {
private Map<String, Strategy> strategies;
void registerStrategy(strategy Strategy) {
strategies.put(strategy.getType(), strategy);
}
void process(obj) {
if (strategies.containsKey(obj.type)) {
// apply might execute db.updateTable1(),
// depending on the interface's implementation
strategies.get(obj.type).apply();
} else {
System.out.println("No strategy registered for type: " + obj.type);
}
}
}
不幸的是,当你在Java、C++、C#等中使用OOP时,你必须处理你所认识到的权衡,因为系统是动态组合在一起的,而SOLID在某种程度上解决了缺陷。但SOLID原则旨在提供指导,我不会习惯性地遵循它们。
我希望能找到一个比我自己更优秀的程序员来演示命令模式的例子。但我只是发现了一些非常糟糕的例子,这些例子并没有真正解决你的问题。
定义将意图(定义为字符串或枚举、按钮单击(与操作(对象、lambda函数(关联的问题总是需要我们必须处理的间接级别。一些抽象层是可以接受的,例如:永远不要直接在视图中调用模型或服务。您还可以考虑实现am事件调度器和相应的侦听器,这将有助于松耦合。但在较低级别,你必须查找所有听众。。。
obj的性质是不明确的,但我建议拥有一个定义良好的接口,并将其传递到整个代码中,其中接口的类实现将等效于您的"操作"。以下是Typescript中的一个示例:
interface someDBInterface {
performAction() : void;
}
function process(obj : someDBInterface) {
let result = obj.performAction();
}
class action1 implements someDBInterface {
status: any
performAction() {
//db.updateTable1();
}
}
class action2 implements someDBInterface {
status : any
performAction() {
//db.updateTable1(this.status);
}
}
class action3 implements someDBInterface {
performAction() {
//May be log action 3 recieved
}
}
如果这不符合您的要求,请随时联系:(