依赖性反转原则 - 为什么不使用基类?

发布于 2025-02-03 12:49:09 字数 1177 浏览 3 评论 0 原文

假设我有一个使用特殊网络套接字的应用程序,该应用程序通过XML格式的套接字传输数据,称为 XMLSocket

XMLSocket 继承 basesocket ,并满足Liskov替代原则。我们可以互换它们。

我有一个客户端应用程序,该应用程序使用 xmlsocket

class Client:
    def start(sock: BaseSocket):
        sock.connect(ip, port)
        sock.send(some_request)
        some_response = sock.recv()

现在,我们可以通过 xmlsocket basesocket baseSocket to client.start(),此代码有效。

我们甚至可以添加更多 basesocket 的推导,例如 jsonsocket ,只要整体流,功能签名和返回类型相同,它仍然可以与层次结构中的任何插座类。然而,这违反了依赖性反转原则。

现在,让我们创建一个接口 isocket

class ISocket(ABC):
    def connect(ip, port):
        pass

    def send(request):
        pass

    def recv() -> Response:
        pass

make client.start()取决于我们的接口,因此满足依赖关系反转原则:

class Client:
    def start(sock: ISocket):
        sock.connect(ip, port)
        sock.send(some_request)
        some_response = sock.recv()

好吧,似乎我们已经完成了与基类同样的事情。只要它的行为就像插座,我们可以通过任何插座的变化。

,为什么它必须取决于抽象,而不是基类?

Let's say I have an application which uses a special network socket, which transmits data over a socket in XML format called XMLSocket.

XMLSocket inherits BaseSocket, and satisfies the Liskov Substitution Principle. We can interchange them.

And I have a client application which uses XMLSocket:

class Client:
    def start(sock: BaseSocket):
        sock.connect(ip, port)
        sock.send(some_request)
        some_response = sock.recv()

Now, we can pass XMLSocket or BaseSocket into Client.start(), and this code works.

We could even add on more derivations of BaseSocket such as JSONSocket, and as long as the overall flow, function signatures, and return types are the same, it would still work with any of our socket classes in the hierarchy. Yet, this violates the dependency inversion principle.

Now let's create an interface ISocket:

class ISocket(ABC):
    def connect(ip, port):
        pass

    def send(request):
        pass

    def recv() -> Response:
        pass

And make Client.start() depend on our interface instead, therefore satisfying the dependency inversion principle:

class Client:
    def start(sock: ISocket):
        sock.connect(ip, port)
        sock.send(some_request)
        some_response = sock.recv()

Well, it seems like we've accomplished the same exact thing as the base class did. We can pass in any variation of a socket we can dream of, as long as it behaves like a socket.

So, why must it depend on an abstraction, instead of a base class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

踏雪无痕 2025-02-10 12:49:09

最好使用抽象类或界面作为:

It is better to use abstract class or interface as:

  • Don't derive from concrete classes. Or, as Scott Meyers puts it in Item 33 of More Effective C++,[8] "Make non-leaf classes abstract." (Admittedly, it can happen in practice - in code written by someone else, of course, not by you! - and in this one case you may have to have a public virtual destructor just to accommodate what's already a poor design. Better to refactor and fix the design, though, if you can.)

  • It is not possible to create instance from abstract class. So it makes easier to add abstract methods. And by using abstract keyword or interface you tell to other developers and compiler to inherit from it to create a concrete useful implementation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文