oop的重用性,可扩展性
There’s a big hole in reusability on the web. An entertaining statistic — not the most accurate but still fascinating — was generated by Simon Wardley from a Twitter poll. He calculated that basic user registration had been written over a million times. The average developer had written user registration about 5 times. I’m sure you’ve built it a few times yourself.
网络上的可重用性有一个大漏洞。 西蒙·沃德利(Simon Wardley)从Twitter的一项民意测验中得出了一种有趣的统计数据-虽然不是最准确,但仍然很有趣。 他计算出基本用户注册已经写了超过一百万次。 平均每个开发人员写用户注册大约5次。 我相信您已经自己构建了几次。
The solution to this would be to create a reusable ‘package’ for user registration. So why isn’t there such a thing? It’s a really interesting question, because if you could fix it, you could make web development so much faster.
解决方案是为用户注册创建可重用的“包”。 那么为什么不存在这样的事情呢? 这是一个非常有趣的问题,因为如果可以解决,则可以使Web开发更快。
三种应用 (Three kinds of application)
The first problem is that user registration has both front end and back end elements. So you would need a package that included both of these. I think the closest to that would be a Wordpress plugin. PHP (on which Wordpress is built) mixes markup and processing on the back end. Although PHP doesn’t have a neat way of packaging a group of pages and back end, you can do this in Wordpress. The problem though is that then you have to do everything else on your site using Wordpress. Empirically, people are still building registration over and over again, so the numbers tell us that’s still quite a problem. But even so, this is key to the huge success of Wordpress on the web.
第一个问题是用户注册同时具有前端和后端元素。 因此,您需要一个包含这两者的软件包。 我认为最接近的是Wordpress插件。 PHP(基于Wordpress构建PHP)在后端混合了标记和处理。 尽管PHP没有包装一组页面和后端的整洁方法,但是您可以在Wordpress中进行此操作。 但问题在于,然后您必须使用Wordpress在网站上进行其他操作。 根据经验,人们仍在不断地建立注册,因此数字告诉我们这仍然是一个问题。 即便如此,这仍然是Wordpress在网络上取得巨大成功的关键。
预建应用程序,插件组件 (Prebuilt application, plugin components)
The limitation Wordpress has is that it’s a container that contains plugins. Whatever you build with Wordpress, it will still be a Wordpress site containing plugins, bits of your code, and your content. It won’t be a mobile app (although it could get close these days), it won’t be a data-backed web application. With Wordpress or with other conventional Content Management Systems, or with systems like Salesforce or Sharepoint, what you have is basically an application which you are able to extend and vary with plugins. You’re limited in what functionality you can reuse with plugins, and because the application around them is fixed, you’re limited in what the final application can do.
Wordpress的局限性在于它是一个包含插件的容器。 无论您使用Wordpress构建什么内容,它都将仍然是一个包含插件,代码段和内容的Wordpress网站。 它不会是一个移动应用程序(尽管这些天可能会关闭),也不会是一个数据支持的Web应用程序。 借助Wordpress或其他常规的内容管理系统,或与诸如Salesforce或Sharepoint之类的系统,您基本上拥有的应用程序可以扩展并随插件而变化。 您在可与插件一起重用的功能方面受到限制,并且由于围绕它们的应用程序是固定的,因此最终应用程序的功能也受到限制。
This is the least flexible kind of environment I would identify for reuse. There are two other more flexible types.
这是我确定要重用的最不灵活的环境。 还有另外两种更灵活的类型。
定制应用程序,库组件 (Custom application, library components)
The next is a module library for a programming language. The classic example I’d use here is NPM, the Node Package Manager, which has a huge range of almost entirely open source packages for programming in Javascript. Most programming languages have some equivalent. NPM itself reports the staggering statistic that its code makes up 97% of web applications which use it. Which if you think about it means that it makes those applications 33 times cheaper to develop.
接下来是用于编程语言的模块库。 我在这里使用的经典示例是NPM,即Node Package Manager,它具有范围广泛的几乎完全开放源代码的程序包,可以使用Java编程。 大多数编程语言都有一些等效语言。 NPM本身报告了惊人的统计数据,其代码占使用它的Web应用程序的97%。 如果您考虑一下,这意味着它使那些应用程序的开发成本降低了33倍。
Clearly this is a very successful model of reuse. It does have some limitations. A code package clearly can only be used in the environment of the language in which it is written. However this is an environment which will let you build almost anything.
显然,这是一个非常成功的重用模型。 它确实有一些限制。 显然,代码包只能在其编写语言的环境中使用。 但是,这种环境可以让您构建几乎所有东西。
Interestingly, despite being much more flexible in this way than something like Wordpress, I’ve never seen a software package for a whole user registration process. The reason is that packages tend to be either for front end or back end. More generally, there’s still (even if it’s only 3% of the total) quite a bit of custom coding required to glue code packages together, and a lot of this glue is very repetitive. This is mainly because code packages need a frame built around them to turn them into applications.
有趣的是,尽管这种方式比Wordpress之类的方式更加灵活,但我从未见过用于整个用户注册过程的软件包。 原因是包装倾向于用于前端或后端。 通常,将代码包粘合在一起仍然需要(即使仅占总数的3%)相当多的自定义编码,并且这种粘合很多都是非常重复的。 这主要是因为代码包需要围绕它们构建的框架才能将它们转换为应用程序。
定制应用程序,应用程序组件 (Custom application, application components)
On to the next reuse environment. This is an environment for composing small applications into larger ones. A well-known example of this is shell scripting in Unix/Linux. It lets you script Linux commands, which are mostly little applications, into a more complex command i.e. application. These scripts are also a very successful model of reuse, having been used widely for decades. They are still in 2020 the most effective way of automating administrative work, a testament to how powerful they are. I don’t know if it’s ever been done, but I would guess the ratio of the lines of code in a script to the lines of code required to write the script’s function from scratch would be better than 1:100.
进入下一个重用环境。 这是一个将小型应用程序组合成大型应用程序的环境。 一个著名的例子是Unix / Linux中的shell脚本。 它使您可以将Linux命令(通常是很少使用的应用程序)编写为更复杂的命令(即应用程序)。 这些脚本也是非常成功的重用模型,已经被广泛使用了数十年。 到2020年,它们仍然是使行政工作自动化的最有效方法,这证明了它们的功能强大。 我不知道它是否曾经完成过,但是我猜想脚本中的代码行与从头开始编写脚本函数所需的代码行之比会比1:100更好。
流水线 (Pipelines)
If we look at the characteristics of Bash scripts, one remarkable property of them is how much work can be done with how little scripting. The model of composition they use is involves creating pipelines. This is a very common idiom widely used in many fields: the key idea is to have a range of functional components with a common interface between them. Obviously plumbing is the source of the term and plumbing components work very much like this. Electrical circuits, even lego bricks make use of this concept. Its value is to create the highest possible degree of interoperability and interchangeability between components since because of the uniform interface, any group of components can be connected together. And also the new component formed by connecting others again has the same interface as an individual component itself.
如果我们看一下Bash脚本的特征,那么它们的一个显着特性就是用很少的脚本就可以完成多少工作。 他们使用的组合模型涉及创建管道。 这是在许多领域中广泛使用的非常常见的习惯用法:关键思想是拥有一系列功能组件,并且它们之间具有通用的接口。 显然,管道是该术语的来源,并且管道组件的工作原理非常像这样。 电路,甚至是乐高积木都利用了这一概念。 它的价值在于在组件之间建立尽可能高的互操作性和互换性,因为由于界面统一,任何组的组件都可以连接在一起。 通过再次连接其他组件而形成的新组件也具有与单个组件本身相同的接口。
三种重用模型 (The 3 models of reuse)
So to summarise we have 3 models of reuse:
总结一下,我们有3种重用模型:
- Application with plugins as components 以插件为组件的应用程序
- Application built from library components从库组件构建的应用程序
- Application built from application-components由应用程序组件构建的应用程序
My argument here is that the last of these 3 is the most powerful. It is much more flexible and general than the first, because you can build any application rather than modifying one existing application by choosing plugins. It requires less effort and expertise than the second, because you don’t have to write much code to glue everything together and to provide the wrapper needed to take a library into being an application. However you retain the flexibility of being able to build anything, especially when you allow for the custom coding of constituent applications. Pipelines are a key means of making it possible to compose applications like this.
我在这里的论点是,这3个中的最后一个是最强大的。 它比第一种更加灵活和通用,因为您可以构建任何应用程序,而不用通过选择插件来修改一个现有应用程序。 与第二种方法相比,它需要较少的工作量和专业知识,因为您不必编写太多代码即可将所有内容粘合在一起,也无需提供将库变成应用程序所需的包装器。 但是,您保留了构建任何东西的灵活性,尤其是当您允许对组成应用程序进行自定义编码时。 管道是使编写此类应用程序成为可能的关键手段。
网络上的管道 (Pipelines on the web)
This all suggests that if there were a way of building pipelines on the web, and composing services to form bigger services, this could be a powerful way of approaching building for the web. The web is built on the client-server model. The client application (often a web browser) makes a request to a server which hosts the service, and receives a response. Requests and responses are both messages in the HTTP protocol.
所有这些都表明,如果存在一种在Web上构建管道并组合服务以形成更大的服务的方法,那么这可能是一种进行Web构建的有效方法。 Web建立在客户端-服务器模型上。 客户端应用程序(通常是Web浏览器)向托管服务的服务器发出请求,并接收响应。 请求和响应都是HTTP协议中的消息。
With only a minor restructure, this fits perfectly into the pipeline model because you have a common interface between your functional units which is the HTTP message. By sending the response from one service as a request to the next, you have a pipeline. And like with all good ideas, this has been done before. In fact with some success, but as yet in a very limited way.
仅需很小的重组,它就非常适合管道模型,因为在功能单元之间有一个公共接口,即HTTP消息。 通过将一项服务的响应作为请求发送到另一项服务,您就拥有了一条管道。 就像所有好主意一样,这已经完成了。 实际上取得了一些成功,但到目前为止仍然非常有限。
雅虎管道 (Yahoo Pipes)
This popular and innovative service was born from the enthusiasm for ‘mashups’ in 2007, combining data from multiple web sites and services. The service was directly inspired by Unix pipes (see the Wiki page about this). It created pipelines from single or multiple sources on the web and supplied built in processors to generate outputs. Sadly shut down in 2015, it inspired services such as IFTTT and Zapier which are popular today. An article on the blog of another service, import.io, describes this change.
这种流行和创新的服务源于2007年对“混搭”的热情,它结合了来自多个网站和服务的数据。 该服务直接受Unix管道启发(有关此信息,请参阅Wiki页面)。 它从Web上的一个或多个源创建了管道,并提供了内置处理器来生成输出。 可悲的是在2015年关闭,它激发了诸如IFTTT和Zapier之类的流行服务。 另一个服务的博客import.io上的一篇文章介绍了此更改。
While Pipes was a successful tool, it was limited in how it fulfilled the possibilities of pipes and application composition on the web.
尽管Pipes是成功的工具,但它在如何实现Web上管道和应用程序组成的可能性方面受到限制。
- While it could take input from the web, it couldn’t output to the web or process using web services 虽然它可以从Web接收输入,但不能输出到Web或使用Web服务进行处理
- While it had the concept of pipes, it didn’t let you use them to link web services.尽管它具有管道的概念,但它不允许您使用它们来链接Web服务。
- It was a closed system, not open to the web. 这是一个封闭的系统,不开放给网络。
IFTTT和Zapier (IFTTT and Zapier)
These platforms which are widely used today work by creating event driven pipelines between services. They often form part of the back end functionality of no code projects. They wrap third party services with a common input/output interface, allowing the building chains of processors to for instance trigger SMS or emails when a certain log message arrives in a logging services. While these services create pipelines out of web services, they still have limitations:
这些今天广泛使用的平台通过在服务之间创建事件驱动的管道来工作。 它们通常构成无代码项目的后端功能的一部分。 它们使用通用的输入/输出接口包装第三方服务,从而允许处理器的构建链在某个日志消息到达日志记录服务时例如触发SMS或电子邮件。 虽然这些服务从Web服务创建管道,但它们仍然有局限性:
- They can connect existing services, but they can’t be used to create new APIs or services. 它们可以连接现有服务,但不能用于创建新的API或服务。
- The wrappers around services are not open and based on HTTP, but closed and only connectable within these platforms. 服务周围的包装不是基于HTTP的开放式服务,而是封闭的,只能在这些平台内连接。
A possible reason for the limitations of these services is the main difficulty with pipelines on the web: latency. Latency is the time between a request and the first arrival of the response. When we get to tens or hundreds of services in a pipeline, this delay builds up to the point where the pipeline is extremely slow. By containing the pipelines being built within the platform, these systems minimise this problem as latency is tiny within the same service. If they were open and the pipeline connections were being made between different services on the web, the much greater latency would become more of an issue.
这些服务受到限制的可能原因是网络上管道的主要困难:延迟。 延迟是从请求到响应首次到达之间的时间。 当我们在管道中获得数十或数百个服务时,这种延迟会累积到管道极慢的地步。 通过包含在平台内构建的管道,这些系统将延迟最小化,因为同一服务中的延迟很小。 如果它们是开放的,并且在Web上的不同服务之间建立了管道连接,那么更大的延迟将成为更多问题。
无服务器功能 (Serverless functions)
Another model which comes close to the ideal of pipelines in the web is the idea of serverless functions such as AWS Lambda. In fact the ‘serverless’ aspect of these functions is not the interesting part in relation to this. The concept of a function which transforms a web request into a web response and which is published on the web and accessible at a URL is a core element of what I am proposing. Many have expected serverless functions to form a basis for reuse on the web, however this hasn’t materialised to any real degree. This is due to these limitations of serverless functions:
另一个与Web管道理想非常接近的模型是无服务器功能(例如AWS Lambda)的想法。 实际上,这些功能的“无服务器”方面并不是与此相关的有趣部分。 将Web请求转换为Web响应并在Web上发布并可以通过URL访问的功能的概念是我提出的核心要素。 许多人期望无服务器功能可以构成在Web上重用的基础,但是这还没有真正实现。 这是由于无服务器功能的这些限制:
- While latency between serverless functions within the same cloud platform is low, they have a so-called ‘cold start’ delay. The platform on which these functions run has to start the code for the function on a physical server every time it is called. It then keeps that code around for a short while before removing it again: if another request for that function comes in during that time it can immediately satisfy it, but otherwise it has to pay the startup cost again. This delay while counted in milliseconds can be signficant and is quite variable. A chain of serverless functions could create a very significant delay. 尽管在同一云平台内无服务器功能之间的延迟很短,但它们具有所谓的“冷启动”延迟。 运行这些功能的平台必须在每次调用时在物理服务器上启动该功能的代码。 然后,它将这段代码保留一会儿,然后再次将其删除:如果在这段时间内收到对该功能的另一个请求,它可以立即满足它,但否则必须再次支付启动成本。 这种延迟以毫秒为单位,可能很明显,并且变化很大。 无服务器功能链可能会造成非常大的延迟。
- Another issue with serverless functions calling one another is that they are paid for on the basis of the time they are running. When one invokes another and it has to wait for the response, both are active and being paid for. This is less of a problem for building pipelines as a pipeline manager can call one function, get the result, end the function and send the result to the next etc. 无服务器功能相互调用的另一个问题是,它们是根据运行时间付费的。 当一个人调用另一个而必须等待响应时,两者都处于活动状态并需要付费。 对于构建管道而言,这并不是什么大问题,因为管道管理器可以调用一个函数,获取结果,结束该函数并将结果发送给下一个,等等。
- The Yahoo Pipes based platforms all provide a number of internal basic services for e.g. transforming data between one step and the next. There’s nothing of this kind for Serverless functions as they were never really conceived of as an environment for composition. 所有基于Yahoo Pipes的平台都提供了许多内部基本服务,例如,在一个步骤和另一个步骤之间转换数据。 无服务器功能没有这种类型,因为它们从未真正被构想为组合环境。
- Setting up a serverless function takes a signficant time and while AWS has a service called Step Functions which helps with composition, working with serverless functions is not nearly as simple as the drag and drop style functionality provided by Yahoo Pipes and friends. 设置无服务器功能需要花费大量时间,而AWS有一项名为Step Functions的服务可帮助进行合成,但使用无服务器功能并不像Yahoo Pipes和朋友提供的拖放样式功能那么简单。
新模式 (The new model)
Combining the best aspects of these prior examples and adding some new ideas leads to a new model of pipelines for the web which resolves their limitations and brings this concept to its full power. A name for this model is a service space.
结合这些先前示例的最佳方面并添加一些新思想,可以创建一种新的Web流水线模型,从而解决了它们的局限性,并将这一概念发挥了最大作用。 此模型的名称是服务空间。
运行 (Runtime)
The functions of this new model are provided by a runtime, i.e. a piece of code which can be installed on different servers with different characteristics, or even run within a serverless function. This means it’s possible either to both provide a platform which manages the runtime and the servers on which it is running (making it a serverless service), or to control how and where the runtime runs yourself, adding more flexibility.
这个新模型的功能由运行时提供,即一段代码,可以将它们安装在具有不同特性的不同服务器上,甚至可以在无服务器功能内运行。 这意味着既可以提供一个管理运行时和运行它的服务器的平台(使其成为无服务器服务),也可以控制运行时如何以及在何处运行,从而增加灵活性。
一台服务器上的多种服务 (Multiple services on one server)
The runtime hosts a number of services on one server in one process. These services have internal routing which lets them message each other with zero overhead. Latency cost only exists for calls from one server to another. This greatly reduces the issue of latency in a complex service pipeline. The services which exist, the url paths on which they are accessed, and their configuration are specified in a configuration file.
运行时在一个进程中在一台服务器上承载许多服务。 这些服务具有内部路由,使它们能够以零开销相互发送消息。 延迟成本仅存在于从一台服务器到另一台服务器的呼叫中。 这大大减少了复杂服务管道中的延迟问题。 在配置文件中指定存在的服务,访问它们的URL路径以及它们的配置。
服务提供HTTP接口 (Services provide an HTTP interface)
The interface to every service is that it receives an HTTP message and emits another HTTP message, allowing them to be pipelined together. HTTP messages are standarised somewhat to improve interoperability. The key standardisation is on JSON as the data representation. HTTP message can then have essentially 3 types of body: binary data, text data or JSON.
每个服务的接口是,它接收一条HTTP消息并发出另一条HTTP消息,从而使它们可以一起通过管道传输。 HTTP消息经过某种程度的标准化,以提高互操作性。 关键的标准化是在JSON上作为数据表示形式。 这样,HTTP消息可以具有3种类型的主体:二进制数据,文本数据或JSON。
服务托管在URL上并向网络开放 (Services are hosted on URLs and open to the web)
Every service on a server is configured on a URL within that server’s domain, and so can be called across the web. Authentication is built-in and can be used to restrict access where required. This means it’s possible to call any service or pipeline made of services like any other service on the web.
服务器上的每个服务都在该服务器域内的URL上配置,因此可以在整个网络上调用。 身份验证是内置的,可用于在需要时限制访问。 这意味着可以像在网络上的任何其他服务一样调用任何服务或由服务组成的管道。
参照透明 (Referential transparency)
The model provides this useful quality of a distributed system which means that if you have a reference to a service (which is its URL), you don’t care about where it is running. This is provided by a simple built-in service that simply forwards a web request from one server to another. This makes it possible to move a service on a runtime on one server to the runtime on another server simply by replacing it with this forwarding service which means the service you moved can still be found on the same URL. This ability to transparently distribute services where you like simplifies scaling and helps to run workloads on appropriate infrastructure.
该模型提供了分布式系统的这种有用的质量,这意味着如果您引用了服务(即它的URL),则无需关心它在哪里运行。 这是由简单的内置服务提供的,该服务简单地将Web请求从一台服务器转发到另一台服务器。 只需将其替换为转发服务,就可以将一台服务器上的运行时服务迁移到另一台服务器上的运行时,这意味着仍可以在同一URL上找到您移动的服务。 这种透明地分发服务的功能可以简化扩展,并有助于在适当的基础架构上运行工作负载。
核心和定制服务库 (Library of core and custom-built services)
The model requires that you have a central public library of service code. A runtime can pull the code for any service it needs to run from this library. The runtime itself contains a core set of services that provide for such things as constructing pipelines from other services, storing data, authentication etc. The library can be extended by user contribution. Private libraries can exist for teams to share their service code.
该模型要求您具有服务代码的中央公共库。 运行时可以从该库中获取需要运行的任何服务的代码。 运行时本身包含一组核心服务,这些服务提供诸如从其他服务构建管道,存储数据,身份验证等功能。可以通过用户贡献来扩展该库。 团队可以使用私有库来共享他们的服务代码。
It will be possible for all or nearly all the services needed to be library services, just leaving the job of configuring the services themselves.
所有或几乎所有服务都需要成为库服务,而无需自己配置服务。
统一的功能接口 (Uniform interfaces to functionality)
Another desirable property of this platform is that it should define consistent APIs to common functionality. For instance, the basic API for data storage should be that data is written to a URL via a PUT request, then read back from the URL via a GET request. The best way to structure this is that a service exists to provide this interface, into which you then plug drivers for different underlying data sources, whether it be file storage, a relational database, a NoSQL database etc. You can then access the more powerful features of the underlying data source such as querying or search though a separate service.
该平台的另一个理想属性是,它应该为通用功能定义一致的API。 例如,用于数据存储的基本API应该是通过PUT请求将数据写入URL,然后通过GET请求从URL读回。 最好的构造方法是存在一个提供此接口的服务,然后您可以在其中插入用于不同基础数据源的驱动程序,这些驱动程序可以是文件存储,关系数据库,NoSQL数据库等。然后,您可以访问功能更强大的数据库。基础数据源的功能,例如通过单独的服务进行查询或搜索。
用户界面 (User interface)
Services contain their own documentation and advertise which consistent APIs they provide. This then enables a modular user interface which is built of modules which provide user interaction with those APIs, as well as overall configuration for runtimes. As an example of a consistent API, many services will store files and data for their use in different ways, and expose these on URLs. The UI should allow those files and data to be navigated, edited, created and deleted.
服务包含其自己的文档,并宣传它们提供哪些一致的API。 然后,这将启用由模块构成的模块化用户界面,这些模块提供用户与这些API的交互以及运行时的整体配置。 作为一致API的示例,许多服务将存储文件和数据以不同方式使用,并将其公开在URL上。 用户界面应允许对这些文件和数据进行导航,编辑,创建和删除。
讨论与范例 (Discussion and example)
As part of creating and investigating this model, a runtime named Restspace has been created (https://restspace.io). The documentation can give an idea of how a platform like this can work in practice. It’s a work in progress and lacks certain aspects of the model as yet.
作为创建和研究此模型的一部分,已创建一个名为Restspace的运行时( https://restspace.io )。 该文档可以让您了解这样的平台如何在实践中工作。 这项工作仍在进行中,尚缺乏模型的某些方面。
It’s striking how fast it is possible to build a wide range of web services, APIs and websites using a system like this.
令人惊讶的是,使用这样的系统构建各种Web服务,API和网站的速度有多快。
So how do you reuse user registration? Here’s a way it could work with Restspace.
那么,如何重用用户注册? 这是它可以与Restspace一起工作的一种方式。
- A special service mediates changes to the configuration of each server. When a service is added via this service, it ensures any other services it depends on are also present in the configuration. 特殊服务介导对每个服务器配置的更改。 通过此服务添加服务时,它确保配置中还存在它依赖的任何其他服务。
- Adding user registration simply involves simply adding a user registration service. 添加用户注册仅涉及简单地添加用户注册服务。
- This provides a url on which there is a script file which builds the registration user interface on the browser. 这提供了一个URL,该URL上有一个脚本文件,该脚本文件可在浏览器上构建注册用户界面。
- As dependencies of the user registration service we have the authentication service and the user store service which are added if necessary when the user registration service is added. 作为用户注册服务的依存关系,我们具有身份验证服务和用户存储服务,如果需要,在添加用户注册服务时会添加它们。
- The script knows where it can find the user store (which includes a JSON Schema which specifies the fields of a user record) and adds a new user record when a site user registers. 该脚本知道可以在哪里找到用户存储(其中包括指定用户记录字段的JSON模式),并在站点用户注册时添加新的用户记录。
结论 (Conclusion)
A number of past and present web platforms have attempted to foster reuse and composition of web services, but have had partial success. The most successful approaches to reuse involve composition of applications. In the realm of the web, composition is best done by the creation of pipelines of web services. A system which fully realises the possibilities in this space needs a number of elements and capabilities in place as listed above. I propose the way forward is the model outlined in this article.
过去和现在的许多Web平台都试图促进Web服务的重用和组合,但是取得了部分成功。 最成功的重用方法涉及应用程序的组合。 在Web领域,最好通过创建Web服务管道来完成组合。 完全实现该空间可能性的系统需要上面列出的许多要素和功能。 我提出前进的道路是本文概述的模型。
Any comments would be very welcome, as I’m fascinated to hear people’s opinions on this.
任何评论都将受到欢迎,因为我着迷于听取人们对此的看法。
翻译自: https://medium.com/@jim_ej/web-reusability-a-new-way-66c578424134
oop的重用性,可扩展性