如何在新错误类型的错误处理中实现开闭原则?

How to implement the Open-Closed Principle in error handling for new error types?

提问人:Ahzam Ahmed 提问时间:5/11/2023 更新时间:5/20/2023 访问量:72

问:

在给定的 JavaScript 代码片段中,有两个函数 handleClientErrorhandleServerError,分别处理客户端和服务器端错误。handleError 函数用于根据传递给它的 error 参数确定需要处理的错误类型。

但是,如果需要处理新的错误类型(假设 ABCSideError),则代码当前需要修改 handleError 函数,以便为 ABCSideError 类型添加新的条件检查,并定义一个新函数来处理它。

如何避免为每个新错误类型添加新检查和函数的手动过程,意味着添加新的错误类型只需要定义一个新函数,而不需要其他任何内容。

try{
  // some code
}catch(error)
{
  handleError(error)
}

function handleError(error) {
  if (error instanceof ClientSideError) {
    return handleClientError(error);
  }
  return handleServerError(error);
}

function handleClientError(error) {
  // handle error from client side
}

function handleServerError(error) {
   // handle error from server side
}

尽管进行了多次尝试,但我仍无法找到不需要 if-elseswitch 语句来动态处理新错误类型的实现。

JavaScript 编码风格的 Solid-Principles 设计 开放-封闭原则

评论

0赞 Teemu 5/11/2023
将错误处理程序函数创建为对象的方法(例如),并根据错误对象的构造函数命名方法,然后调用正确的方法,如 。errorHandlerserrorHandlers[error.constructor.name](error);

答:

0赞 John Deters 5/12/2023 #1

开闭原则与“if-else”或“switch”语句无关。OCP 规定,类应该开放以进行扩展,但可以关闭以进行修改。换句话说,不要让新行为从你当前的课堂中逃脱,从而破坏你的客户。就是这样。

通过封装错误处理并且不引发或重新引发新错误,您已经实现了 OCP 的目标,即不对当前依赖此代码的客户端进行重大更改。

避免使用“if-else”和“switch”语句是重构过程的一部分,在重构过程中,它们被视为“代码异味”,表明某种形式的重构可能是可取的。但这并不完全适用于这里,因为这是外部库的错误处理程序,这些库会引发超出您控制范围的错误。您没有机会修改它们。相反,错误处理程序将成为封装它们的位置,因此新错误不会违反 OCP。如果这需要额外的“if instanceof”测试,那么这就是你必须编写的内容。

0赞 Tando 5/12/2023 #2

下面是一个示例。如果您有大量且不断增长的错误类型,这可能是一种方法。

class ErrorHandler {
  handleError(error) {
    // This will be overridden in subclasses
  }
}

class ClientErrorHandler extends ErrorHandler {
  handleError(error) {
    if (!(error instanceof ClientSideError)) {
      return null;
    }
    // handle error from client side
  }
}

class ServerErrorHandler extends ErrorHandler {
  handleError(error) {
    if (!(error instanceof ServerSideError)) {
      return null;
    }
    // handle error from server side
  }
}

然后,在调用该函数时,您将 DI 处理程序数组。这样,即使你有新的具体错误处理程序,riskyOperation 也不需要修改。

function riskyOperation(errorHandlers) {
  try {

    // do stuff ...

  } catch (error) {
    for (let handler of errorHandlers) {
      if (handler.handleError(error)) {
        break;
      }
    }
  }
}

riskyOperation([new ClientErrorHandler(), new ServerErrorHandler()]);

OCP 的关键概念包括:

  • 抽象化
  • 多态性
  • 客户端和服务器/服务的范围

然后,即使服务数量增加,客户端也不需要修改。

0赞 root 5/20/2023 #3

消费者代码:

try {
  // ...some code...
} catch(error) {
  handleError(error)
}

错误处理中心:

function handleError(error) {
  for (let handler of errorHandlerRegistry.handlers) {
    let r = handler(error);
    if (r) {
      return r;
    }
  }
  // ...some default behavior...
}

let errorHandlerRegistry = {
  handlers: [],
  register: function(handler) {
    this.handlers.push(handler);
  },
};

客户端错误处理程序:

function handleClientError(error) {
  // ...handle error from client side...
}
errorHandlerRegistry.register(handleClientError);

服务器错误处理程序:

function handleServerError(error) {
   // ...handle error from server side...
}
errorHandlerRegistry.register(handleServerError);

ABC 错误:

function handleAbcError(error) {
   // ...handle error from ABC side...
}
errorHandlerRegistry.register(handleAbcError);