清洁代码_清洁建筑-简介

清洁代码

介绍 (Introduction)

With the constant evolution in the software development process and the growing adoption of different frameworks, is becoming very common developers getting comfortable with the structures provided by these tools and leaving aside some principles of good software development. We need to keep in mind that these tools are just the path and not the end. Our software needs to be more “Domain Oriented Software” and less “Framework Oriented Software”.

随着软件开发过程的不断发展和不同框架的采用,越来越常见的开发人员对这些工具所提供的结构感到满意,并抛弃了一些良好的软件开发原则。 我们需要记住,这些工具只是路径,而不是终点。 我们的软件需要更多的是“面向域的软件”,而不是“面向框架的软件”。

That’s not a criticism with the frameworks and libraries adoptions, they really need to be used, there are a lot of wonderful projects that are here to help us, but we should use them in a way that these tools are adapted to our solution, and not the opposed.

这并不是对框架和库采用的批评,它们确实需要使用,这里有很多很棒的项目可以为我们提供帮助,但是我们应该以使这些工具适合我们的解决方案的方式来使用它们,并且不反对。

Our domain does not need to know what web framework or what database system we are using, these things are just plugins that we may define later.

我们的域不需要知道我们正在使用什么Web框架或数据库系统 ,这些东西只是我们稍后可以定义的插件。

Thinking about this problem, many solutions have been created and one of these is the “Clean Architecture”, presented by Uncle Bob.

考虑到这个问题,已经创建了许多解决方案,其中之一就是Bob叔叔提出的“清洁架构”。

That post will give you a little introduction about Clean Architecture, your main concepts, and a way to implement it, giving us an example of an application build with ReactJS.

该文章将为您简要介绍Clean Architecture,您的主要概念以及实现它的方法,并为我们提供一个使用ReactJS构建应用程序的示例。

清洁建筑 (Clean Architecture)

The main purpose of the Clean Architecture is the Dependency Rule, this rule is all about the direction that our dependencies should point to, that is, always to the high-level policies.

清洁体系结构的主要目的是依赖关系规则,该规则全部关于我们的依赖关系应指向的方向,即始终指向高级策略。

The high-level policies are defined as the core of our application, the components that are independent of any programming language or technology, the policies that only need to change when our domain changes, that is, only in very specific cases.

高级别策略被定义为我们应用程序的核心,独立于任何编程语言或技术的组件,仅在我们的域发生变化时(即仅在非常特殊的情况下)才需要更改的策略。

In contrast, the less specific the component is, the lower the level will be. Here we may put the repositories that connect to our database, the HTTP client that made requests, the presentation layer responsible for the UI, and some components that need to talk with third party libraries for example.

相反,组分的特异性越低,水平将越低。 例如,我们可以在这里放置连接到数据库的存储库,发出请求的HTTP客户端,负责UI的表示层以及一些需要与第三方库进行通信的组件。

We can see that the low-level policies are responsible for things that aren’t specific to our domain but specific for our application, and the application is just the way we choose to solve our domain problem.

我们可以看到,低级策略负责的事情不是特定于我们的域,而是特定于我们的应用程序,而应用程序正是我们选择解决域问题的方式。

Figure 1 exemplifies how the Dependency Rule works.

图1举例说明了依赖性规则的工作方式。

Image for post
Figure 1 — Dependency Rule
图1 —依赖规则

As you may see, all the externals agents point in the same direction, and that pattern may give some benefits to us.

您可能会看到,所有外部代理都指向同一方向,这种模式可能给我们带来一些好处。

Our entities and use cases don’t have any external world dependency, the only concern that they have is about the domain itself.

我们的实体和用例没有任何外部世界的依存关系,它们唯一关心的就是域本身。

So, if we need to change any external agent implementation like an HTTP client, we don’t need to change anything in our use cases, just in the HTTP client concrete class implementation.

因此,如果我们需要更改任何外部代理实现(例如HTTP客户端),则无需在用例中进行任何更改,只需在HTTP客户端具体类实现中进行任何更改即可。

That’s another crucial point in the Clean Architecture, all cross-layer communication is made through solid interfaces. Dependency Inversion has a crucial role in that design, as a matter of fact, we only can get the most of Clean Architecture with we know how to implement all the SOLID principles correctly.

这是Clean Architecture中的另一个关键点,所有跨层通信都是通过可靠的接口进行的。 依赖倒置在该设计中起着至关重要的作用,事实上,只有知道了如何正确实施所有SOLID原则,我们才能充分利用Clean Architecture。

(Example)

Let’s say we have a use case responsible to send a picture to the server. To accomplish that use case, we decided to build a front-end client application with React + Typescript.

假设我们有一个用例负责将图片发送到服务器。 为了完成该用例,我们决定使用React + Typescript构建一个前端客户端应用程序。

We also decided to follow some implementation of the Clean Architecture structure proposal, so, our application will have the following layers:

我们还决定遵循“清洁体系结构”结构提案的一些实现,因此,我们的应用程序将具有以下几层:

Domain Layer: The Domain Layer is the most high-level policy that we have, is where we define the entities and the use cases. We don’t have any concrete class in that layer, only interfaces. All the use case implementations will be in the next layer, the Data.

域层:域层是我们拥有的最高级的策略,是定义实体和用例的地方。 在该层中,我们没有任何具体的类,只有接口。 所有用例实现将在下一层,即数据。

Data Layer: Right behind the Domain Layer, this layer is responsible to implement all the use cases and to define protocols (interfaces) those use case needs. The protocols defined in that layer will be implemented on the next, the Infra.

数据层:在域层之后,该层负责实现所有用例并定义这些用例需求的协议(接口)。 该层中定义的协议将在下一个Infra上实现。

Infra Layer: That Layer will implement the protocols defined in the Data, normally, those implementations are designed to the external world communications, such as database operations, HTTP requests, third-party libraries, and so on. If we think of a client application, like a React app, in that layer we have the HTTP clients, for example.

底层:该层将实现数据中定义的协议,通常,这些实现是针对外部通信而设计的,例如数据库操作,HTTP请求,第三方库等。 如果我们想到一个客户端应用程序(例如React应用程序),则在该层中,例如,会有HTTP客户端。

Looking with more attention to Figure 1, we can see that the Infra Layer belongs to the outer circle, in that circle we do have concrete implementations of libraries and frameworks, it is the most low-level policy that we have, those that aren’t a domain-specific problem.

更加关注图1,我们可以看到Infra层属于外部圈子,在这个圈子中,我们确实有库和框架的具体实现,这是我们拥有的最底层的策略,那些不是一个特定领域的问题。

In that circle, we also have the Presentation Layer, responsible for the UI. Here, in our example, that layer will implement all the React code.

在那个圈子里,我们还有表示层 ,负责UI。 在这里,在我们的示例中,该层将实现所有React代码。

Separating the things in that way, we can see that our core code is completely independent of the library that we choose. We only see the React in one layer, only making what it is designed to do, leaving the business code to other layers. Isolating the app like that, we have many more benefits than coupling all the domain code in our UI.

以这种方式分离事物,我们可以看到我们的核心代码完全独立于我们选择的库。 我们仅将React放在一层,只完成其设计工作,将业务代码留给其他层。 像这样隔离应用程序,与在用户界面中耦合所有域代码相比,我们拥有更多的好处。

实作 (Implementation)

Following our example, first, we need a use case responsible to send images to a server. Let’s call him SendImage.

按照我们的示例,首先,我们需要一个用例,负责将图像发送到服务器。 我们称他为SendImage。

SendImage use case
SendImage用例

As you can see, our use case is just an interface that defines a method send and returns a Promise of a DetectedImage (a specific entity to our domain).

如您所见,我们的用例只是一个接口,该接口定义了发送方法并返回DetectedImage的Promise(对我们域而言是特定实体)。

Now that we have our use case, we need an implementation for him, let’s create a RemoteSendImage in our Data Layer.

现在我们有了用例,我们需要一个实现,让我们在数据层中创建一个RemoteSendImage。

RemoteSendImage
远程发送图像

Some important things to notice about that implementation:

有关该实现的一些重要注意事项:

  • In the class constructor, via dependency injection, we are defining that this class needs an implementation of a HttpPostClient, and a URL.

    在类构造函数中,通过依赖项注入,我们定义了该类需要HttpPostClient和URL的实现。
  • Those dependencies are specific to that implementation. If we have another implementation that does not send the image over HTTP, the dependencies will be another.

    这些依赖关系特定于该实现。 如果我们有另一个不通过HTTP发送图像的实现,则依赖关系将是另一个。

Now, that we defined that we need a HttpPostClient protocol, let’s create in the Data Layer too.

现在,我们定义了我们需要的HttpPostClient协议,我们也要在数据层中创建它。

HttpPostClient protocol
HttpPostClient协议

Just like our use case, that protocol is just an interface that defines a post method. For that implementation, the axios library was chosen, now we create an AxiosHttpClient concrete class that implements the HttpPostClient.

就像我们的用例一样,该协议只是定义post方法的接口。 对于该实现,选择了axios库,现在我们创建一个实现HttpPostClient的AxiosHttpClient具体类。

AxiosHttpClient
AxiosHttpClient

An important thing to notice about that implementation is his interface. Why we didn’t create a more generic interface, instead of one that only knows POST request (HttpPostClient)?

关于该实现的重要注意事项是他的界面。 为什么我们没有创建一个更通用的接口,而不是只知道POST请求的接口(HttpPostClient)?

At that moment, we only need to make POST requests, our use case implementation doesn’t tell not about any GET or PUT requests. So, following the Interface Segregation Principle, we make an extremely cohesive interface, that only knows the method that we need at the moment. If in the future we need to make a GET request for example, we define a protocol for that and make our AxiosHttpClient implement that interface too.

那时,我们只需要发出POST请求,我们的用例实现不会告诉您任何GET或PUT请求。 因此,遵循接口隔离原则,我们创建了一个非常紧密的接口,它只知道当前所需的方法。 例如,如果将来需要发出GET请求,则可以为此定义一个协议,并让AxiosHttpClient也实现该接口。

表示层 (Presentation Layer)

Now it’s the time that React comes in. In our Presentation Layer, we will define a functional component that receives a SendImage use case via dependency inversion in their props.

现在到了React的时候了。在我们的Presentation Layer中,我们将定义一个功能组件,该组件通过其props中的依赖关系反转来接收SendImage用例。

PhotoUpload FC component
PhotoUpload FC组件

There are some important things to notice here:

这里有一些重要的注意事项:

  • A React functional component that only used hooks for your internal logic;

    一个React功能组件,只使用了内部逻辑的钩子;

  • The fact that the component receives an implementation of SendImage in his props is one of the things that make the app structure more flexible;

    组件在其道具中接收到SendImage的实现这一事实是使应用程序结构更灵活的原因之一。
  • The first advantage of this structure is that our UI component doesn’t know anything about sending an image to the server, he only needs someone that knows how to the that. Making your only concern about the UI. That’s the Single Responsibility Principle.

    这种结构的第一个优点是,我们的UI组件对将图像发送到服务器一无所知,他只需要知道如何做到这一点的人。 使您只关心UI。 那就是单一责任原则。
  • Another advantage is that if in the future we decide to send the images over another protocol, we only change the use case implementation, the UI will not even notice that change. With the help of the polymorphism, we can change the implementation easily (Liskov Substitution).

    另一个优点是,如果将来我们决定通过其他协议发送图像,则仅更改用例实现,UI甚至不会注意到该更改。 借助多态性,我们可以轻松更改实现(Liskov替代)。

At the end, our folder structure will be like:

最后,我们的文件夹结构将类似于:

Image for post
Figure 2 — Folder structure
图2 —文件夹结构

结论 (Conclusion)

Unlike most of the examples that we can find on the internet, this implementation provides a very different approach, where the focus of our UI is only the presentation logic. We were able to create a component completely independent of our domain logic and vice versa.

与我们可以在Internet上找到的大多数示例不同,此实现提供了一种非常不同的方法,其中UI的重点只是表示逻辑。 我们能够创建一个完全独立于域逻辑的组件,反之亦然。

If we have a use case that needs to send an image to the server, it doesn’t matter if we use a page created in React, Angular, or Vue, our domain is not concerned with that, it is only concerned with the core of the application. Our domain needs to work in the same way regardless of the framework or libraries we are using. If a more delicate exchange is ever needed, we can do it without having to invest a lot of time in it.

如果我们有一个用例需要将图像发送到服务器,那么我们使用在React,Angular或Vue中创建的页面都没关系,我们的域并不关心这一点,它只关心核心的应用程序。 无论我们使用什么框架或库,我们的域都需要以相同的方式工作。 如果需要更精细的交流,我们可以在不花费大量时间的情况下进行交流。

From the moment that we have a more flexible and robust architecture, in addition to being strongly abstracted, we are able to observe in practice the benefits of object orientation and its principles being fulfilled. At first, it may seem too much work, and it is indeed, because it is necessary to create many protocols and implementations, however, in the long run, it is a trade-off that is really worthwhile.

从拥有更加灵活和健壮的体系结构的那一刻起,除了被严格抽象之外,我们还能够在实践中观察到面向对象及其原理得到满足的好处。 乍一看,这似乎工作太多,的确如此,因为有必要创建许多协议和实现,但是,从长远来看,这是一个折衷,这确实是值得的。

重要连结 (Important Links)

翻译自: https://medium.com/swlh/clean-architecture-a-little-introduction-be3eac94c5d1

清洁代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值