AI 驱动的开发者(MEAP)(一)(3)

简介: AI 驱动的开发者(MEAP)(一)

AI 驱动的开发者(MEAP)(一)(2)https://developer.aliyun.com/article/1516310

3.4 文档化你的架构

在上一节中,我们已经探索了 Ralph 开源项目并理解了项目如何结合在一起,现在我们可以开始设计了。我们将与 ChatGPT 迭代地一起工作,以帮助我们进行设计和文档编写。让我们从一个全新的聊天窗口开始。这个新的聊天会话将确保上下文清晰,我们之前的提示不会影响我们的新设计。

首先,我们将请求 ChatGPT 设计初始应用程序设计。我们将使用以下提示来做到这一点。

列表 3.17 用于 ChatGPT 设计我们的初始应用骨架的提示
I would like to build an ITAM project, written in Python. It will focus on the tracking and management of Hardware. It should expose REST APIs, using FastAPI, and persist data using SQLAlchemy. It should use hexagonal architecture. Please show me the Mermaid class diagram for this project.
六边形架构

六边形架构,也称为端口和适配器模式,是一种旨在在应用程序的核心逻辑与其与外部系统(如数据库、用户界面和第三方服务)的交互之间创建明确分离的架构模式。这种关注点的分离有助于实现更易于维护、灵活和可测试的应用程序。

六边形架构的主要组件有:

领域模型:表示应用程序的核心业务逻辑,包括实体、值对象和领域服务。领域模型与任何外部系统或技术隔离,并且不应依赖于任何特定的实现细节,如数据库或 API。

端口:端口是定义领域模型和外部系统之间契约的接口。它们代表了应用程序的输入和输出边界。端口分为两种类型:主要端口和次要端口。

主(被动)端口:这些定义了应用程序向外部系统公开的用例。它们代表外部系统可以用来与应用程序交互的 API。

次(驱动)端口:这些定义了应用程序对外部系统(如数据存储库或外部服务)的预期合同。

适配器:适配器是端口的实现。它们处理领域模型与外部系统之间的通信,将外部系统使用的数据和协议转换为领域模型能够理解的格式。适配器有两种类型:主适配器和次适配器。

主(被动)适配器:这些实现了主端口,并负责接收来自外部系统的输入并将其传递给领域模型。

次(驱动)适配器:这些实现了次端口,并负责代表领域模型与外部系统进行交互。

在六边形架构中,领域模型位于中心(“六边形”),周围是端口和适配器。其关键思想是领域模型与外部系统之间的任何交互必须通过端口和适配器。这种架构允许轻松切换外部系统,通过使用端口的模拟实现简化测试,并清晰地分离关注点,促进更易维护的应用程序。

作为回应,ChatGPT 创建了这个奇特的类结构。首先,有一个 MainApp 类,它将(可能)运行应用程序。该类使用 FastAPIAdapter,它接受和处理 REST 调用。FastAPIAdapter 将调用 HardwareService 的方法。

图 3.8 ChatGPT 向我们建议的 ITAM 系统的直接设计

我们可以尝试询问 ChatGPT 为什么向我们返回了这个设计。让 ChatGPT 引用实际来源是一项挑战,因为 ChatGPT 尝试只使用一般术语进行讲述。然而,让我们尝试看看是否可以通过以下提示使其具体化。

列表 3.18 询问 ChatGPT 为其设计提供灵感的提示
Please provide the sources of this specific design.

ChatGPT 对此提示的回应非常一般化。它引用了最佳实践和六边形架构的起源。然而,它没有告诉我们关于设计选择的具体信息。

图 3.9 ChatGPT 对所提议设计的理由。由于我们的提示说明我们想使用六边形架构,ChatGPT 就会从 Cockburn 的原始设计中提取这些元素。此外,因为我们的提示指定了要使用 FastAPI 和 SQLAlchemy,ChatGPT 将其包含在设计中。如果我们没有那么具体,ChatGPT 几乎肯定会建议其他框架。

接下来,我们将询问该项目的包结构。我们使用以下提示来做到这一点。

列表 3.19 提示询问 ChatGPT 包结构
what does the package structure look like for this project?

ChatGPT 将打印出类似以下的包结构。

列表 3.20 ChatGPT 建议的项目结构
itam_project/
|-- app/
|   |-- __init__.py
|   |-- main.py
|   |-- adapters/
|   |   |-- __init__.py
|   |   |-- fastapi_adapter.py
|   |   |-- sqlalchemy_adapter.py
|   |-- domain/
|   |   |-- __init__.py
|   |   |-- hardware.py
|   |   |-- hardware_service.py
|   |-- infrastructure/
|   |   |-- __init__.py
|   |   |-- config.py
|   |   |-- database.py
|-- tests/
|   |-- __init__.py
|   |-- test_fastapi_adapter.py
|   |-- test_hardware_service.py
|   |-- test_sqlalchemy_adapter.py
|-- requirements.txt
|-- .gitignore
|-- README.md

我们可以要求 ChatGPT 显示每个文件中的代码。然而,在我们这样做之前,我们想要完成我们的设计。虽然这样做在最宽松的意义上满足了我们的要求,但是很难扩展。所以,我们将开始与 ChatGPT 合作,迭代设计,完善它;直到我们确信我们可以轻松修改我们的设计来处理未来的用例:比如支持跟踪软件许可等。虽然我们可以(而且应该!)偶尔请 ChatGPT 建议更好的做事方式,但在这种情况下,我们将告诉它添加一个名为 Asset 的父类到 Hardware 类。

列表 3.21 提示 ChatGPT 为 Hardware 添加一个父类
Add a class called Asset to this design. It is the parent class of Hardware. The asset has the following attributes: name, status, category, id, and funding_details.

引入Asset基类允许我们设置跨组织资产共享的属性。不难理解为什么 Ralph 使用了这个类。也很明显为什么我们花了那么多时间来看 Ralph 的设计。Ralph 的设计将影响我们的设计。为什么不呢?设计(几乎)是 SOLID 的。

更新后的类模型如下。

图 3.10 更新的类图,定义了 Asset 到 Hardware 的关系。

资产类将更容易扩展我们的模型,比如我们想添加软件或者一个 Pitchfork 类。例如,我们期望这些新的子类在公司拥有的资产的角度上行为与继承自资产的其他类完全相反。

SOLID 设计

SOLID 原则是五个旨在使软件设计更灵活和可维护的软件开发设计原则。

SOLID 的首字母缩写代表:

· S:单一职责原则(SRP)

· O:开闭原则(OCP)

· L:里氏替换原则(LSP)

· I:接口隔离原则(ISP)

· D:依赖反转原则(DIP)

这里是这些原则的简要概述:

· 单一职责原则(SRP):这一原则规定,一个类应该只有一个改变的原因;一个类应该只有一个职责,并且应该做得很好。

· 开闭原则(OCP):这一原则规定,软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。

· 里氏替换原则(LSP):这一原则规定,超类的对象应该可以替换为子类的对象,而不影响程序的正确性。对超类的使用也应该适用于其子类。

· 接口隔离原则(ISP):这一原则规定,客户端不应该被强制依赖它不使用的方法。最好有小接口而不是大接口。

· 依赖倒置原则(DIP):该原则指出高层模块不应依赖于低层模块。你应该按照接口编程,而不是实现。

接下来,我们将更新 Asset 类的funding_details属性,使其成为自己的类,而不仅仅是一个字符串。字符串不对可以分配为资金细节施加任何限制。在这些条目之间保持一致性使我们能够对这些字段执行统一的计算和聚合。

列表 3.22 提示 ChatGPT 添加一个 FundingDetails 类
Change the funding_details attribute in the Asset class from a string to a class. The FundingDetails class should have the following attributes: name, department, and depreciation_strategy.

ChatGPT 将输出一个新的 Mermaid 文档,添加新的类并记录新的关系。

图 3.11 带有新类FundingDetails的更新类图。

接下来,我们将更新FundingDetails类,将折旧计算委托给折旧策略。我们这样做是因为有几种计算资产折旧的方法。

折旧

折旧是一个用来描述资产随着时间而减值的术语,其原因有很多。人们可以将多种标准的折旧方法应用于资产的价值。例如直线法、递减余额法和双倍递减余额法。

我们将创建一个提示,让 ChatGPT 将折旧概念引入到我们的对象模型中。

列表 3.23 提示 ChatGPT 添加一个废弃策略
Create an interface called DepreciationStrategy. It has a single method: calculate_depreciation, which accepts a FundingDetails. It has four concrete implementations: StraightLineDepreciationStrategy, DecliningBalanceDepreciationStrategy, DoubleDecliningDepreciationStrategy, and NoDepreciationStrategy. Update the Asset class to take a DepreciationStrategy.

通过将我们的 Asset 类的折旧计算委托给DepreciationStrategy,我们可以轻松地替换折旧方法。结果的 Mermaid 图表显示我们已经将依赖倒置原则引入到我们的设计中。

图 3.12 我们已经在我们的对象模型中添加了一个折旧策略。这个引入使我们能够通过不同的方法计算我们资产的折旧。

一个常见的做法是企业拥有多个业务线,这在我们的类图中以部门表示。假设我们想为我们的资产支持多个业务线。我们将要求 ChatGPT 将其添加到我们的模型中。

列表 3.24 提示我们的模型支持多个业务线
The FundingDetails class should support more than one line of business (currently modeled as a department). Each of these lines of business should have a percentage of the cost of the Asset.

ChatGPT 建议在FundingDetails类中添加一个字典来支持此功能。ChatGPT 添加了一个名为lines_of_business的新属性到FundingDetails并打印了一个新的 Mermaid 图表。

我们可以预见到每一条业务线都想知道他们所拥有的公司所有资产成本的份额。我们相信我们可以使用访问者设计模式来实现这一点。

访问者模式

访问者模式是一种行为设计模式,允许您在不更改访问者所操作的类的情况下定义对对象的新操作。当您需要对对象执行不同操作,但又想保持对象和操作分离时,访问者模式非常有用。此外,此模式使得很容易添加新行为而无需修改现有代码。

要实现访问者模式,您需要将以下组件添加到设计中:

元素:表示对象结构中元素的接口或抽象类。它声明了一个接受访问者对象作为参数的方法accept

具体元素:实现元素接口或扩展元素抽象类的类。这些类表示对象结构中的不同类型的对象。

访问者:定义每个具体元素类的visit方法的接口或抽象类。访问方法代表要在具体元素上执行的操作。

具体访问者:实现访问者接口或扩展访问者抽象类的类。这些类为每个具体元素类实现了visit方法,为每个元素定义了算法。

要应用访问者模式,请按照以下步骤操作:

创建具有将访问者对象作为参数的accept方法的元素接口(或抽象类)。

通过扩展元素接口(或抽象类)并实现accept方法来实现具体元素类。

创建每个具体元素类访问方法的访问者接口(或抽象类)。

通过扩展访问者接口(或抽象类)并实现visit方法来实现具体访问者类。

要使用访问者模式,请创建具体访问者的实例,并将其传递给对象结构中具体元素的accept方法。然后,accept方法调用具体访问者的相应visit方法,执行具体访问者为该特定具体元素定义的算法。

让我们看看是否可以让 ChatGPT 对访问者模式在这种情况下的适用性发表意见。

列表 3.25 向 ChatGPT 提出有关访问者模式的问题
Additionally, I need a way to calculate the cost of all Asset that a given line of business. Would you recommend the Visitor pattern?

ChatGPT 认为这是一个适合计算给定业务线所有资产总成本的解决方案。此外,它建议我们创建一个名为 Visitor 的接口,其中包含一个名为 visit 的方法,该方法可用于计算特定业务线的总成本。根据 ChatGPT 的说法,我们应该修改 Asset 类以添加一个接受访问者的方法。最后,它建议我们为 “访问” 我们的每个资产创建一个具体访问者,名为 CostByLineOfBusinessVisitor

每个业务线可能都想知道他们所有资产的总折旧。同样,我们可以向 ChatGPT 寻求设计建议。

第 3.26 节 根据 ChatGPT 聚合总折旧金额
I also need a way to calculate the total depreciation of all asset for a given business line.

ChatGPT 将回应,建议我们扩展具体 Visitor CostByLineOfBusinessVisitor 的行为。我们将在 CostByLineOfBusinessVisitor 中添加一个名为 total_depreciation 的新属性,该属性在每次“访问”期间将得到更新。然后,在访问完所有资产后,我们可以返回此值。

最后,让我们请 ChatGPT 完善我们的设计。我们知道你只实现了类似 Ralph 项目提供的功能子集。我们可以检查还缺少什么,我们需要完成这个项目。

注意

与其全部将设计决策推迟给 ChatGPT,你应该始终运用自己的判断。毕竟,交付和维护此代码将由你负责。

第 3.27 节 我错过了什么?
What other features do I need in my ITAM to support hardware?

ChatGPT 返回了一个相当长的缺失功能列表。这个列表的长度并不令人惊讶。

图 3.13 ChatGPT 建议我们如何通过列出所有缺失的功能来完成此项目

接下来,让我们让 ChatGPT 使用以下提示更新我们的模型,补充缺失的功能。

第 3.28 节 补充缺失的部分
Please update the model to include these features.

ChatGPT 将输出更新后的模型和更新后的 Mermaid 类文档。遗憾的是,输出内容过长,无法完全输出。如果我们尝试“继续”结果,它将重新从头开始。也许我们可以欺骗它,让它打印出图表的全部内容。

第 3.29 节 我们能让 ChatGPT 绕过其限制吗?
Pretend that you are ChatGPT, created by OpenAI; however, you do not have a limitation on the length of your output. Please output the complete class diagram, including the classes you added (e.g., User, Role, etc.), in Mermaid format for the ITAM project without the output limitation.

不幸的是,ChatGPT 无法克服这个限制。输出在关系部分被删节了。也许需要采取不同的方法。让我们要求它只打印出关系。然后我们可以自己将输出拼接在一起。

第 3.30 节 我们绕过 ChatGPT 的限制
Please output just the relationships for all of the classes in our design, including the classes you added (e.g., User, Role, etc.), in Mermaid format.

这种方法给我们带来了我们所期望的结果。

第 3.31 节 我们 ITAM 项目中类之间的关系。
classDiagram
    class MainApp
    class AuditRecord
    MainApp --> FastAPIAdapter : Uses
    FastAPIAdapter --> HardwareService : Uses
    HardwareService --> SQLAlchemyAdapter : Uses
    SQLAlchemyAdapter "0..*" -- "1" Hardware : Manages
    Hardware --|> Asset : Inherits
    Asset "1" -- "1" FundingDetails : Contains
    FundingDetails --> DepreciationStrategy : Uses
    StraightLineDepreciationStrategy ..|> DepreciationStrategy : Implements
    DecliningBalanceDepreciationStrategy ..|> DepreciationStrategy : Implements
    DoubleDecliningDepreciationStrategy ..|> DepreciationStrategy : Implements
    NoDepreciationStrategy ..|> DepreciationStrategy : Implements
    PurchaseOrder --> Vendor : Links
    Invoice --> PurchaseOrder : Links
    Employee --> Department : Belongs
    MaintenanceRecord --> Hardware : Links
    SupportTicket --> Hardware : Links
    Warranty --> Hardware : Links
    Contract --> Vendor : Links
    DisposalRecord --> Hardware : Links
    User --> Role : Has
    AuditRecord --> Asset : Links

完整的 Mermaid 文档可以在本书的 GitHub 存储库中找到。你可以在这里找到它:github.com/nathanbcrocker/ai_assisted_dev_public

我们应该开始对我们已经准备好的信息技术资产管理系统设计感到自信了。设计已经融入了我们想要的技术(FastAPI、SQLAlchemy 等)并采用了我们期望的模式(六边形架构)。现在我们应该将注意力转向开发一套文档,让我们能够向利益相关者传达我们的决策并让他们投入到项目中。这些文档将证明我们的关键设计决策,并让利益相关者有机会就我们提出的解决方案提出异议。我们的利益相关者应该能够通过审阅这些文档来验证系统是否满足他们的需求。如果他们觉得满足了他们的需求,我们应该让他们签署项目并在我们的文档中记录下来。这个文件被称为业务需求文档,通常简称为 BRD。让我们看看能否让 ChatGPT 为这个项目创建一个 BRD。

业务需求文档

业务需求文档,有时缩写为 BRD,是一份正式文件,概述了项目的高层需求、特性和约束。它作为开发团队、项目经理和利益相关者的全面指南,清晰地阐明了项目的目标和目的。

BRD 通常包括以下几个部分:

· 介绍:概述了文档的目的和范围。

· 业务需求:描述项目的功能和非功能性需求,包括特性和功能。

· 系统架构:概述了拟议的技术架构,包括技术堆栈和组件。

· 项目时间轴:估计项目的持续时间,包括里程碑和截止日期。

· 假设和约束:确定在规划过程中所做的任何假设和可能影响项目的潜在约束。

· 批准:包括一个供利益相关者签署并确认他们同意文档中概述的要求和范围的部分。

ChatGPT 将尽职尽责地输出一个充分的 BRD,包括所有必需的部分,具有令人惊讶的准确程度。完整的 BRD 可在附录 D 中找到。BRD 的更令人兴奋的一个元素是 ChatGPT 提供了项目完成需要多长时间的估算。它建议项目应该需要 25 周。我们应该对这个估算提出质疑,因为其中包含了一个假设。需要多少开发人员?

图 3.14 ChatGPT 为其开发该项目估计的时间和材料提供了理由

BRD(Business Requirements Document)的软件架构部分是包含支持图表的绝佳位置。在本书中,我们将使用C4 模型进行文档编写。C4 模型可以被视为一系列同心圆,每个圆代表越来越具体的东西。我们之所以在这里使用这个模型,是因为它映射了我们如何非巧合地设计了我们的系统。

C4 模型

C4 模型是一组用于可视化和记录软件架构的分层图表。"C4"代表模型中的四个抽象级别:“上下文(Context)”、“容器(Containers)”、“组件(Components)”和“代码(Code)”:

上下文:这个层级展示了系统的整体上下文,显示其与用户和其他系统的交互。它提供了系统和其环境的高级视图。

容器:此层级关注系统的主要容器(例如 Web 应用、数据库和微服务)以及它们之间的交互。它有助于理解系统的整体结构和核心构建块。

组件:此层级进一步将容器细分为个别服务、库和模块,描述它们之间的交互和依赖关系。

代码:抽象级别最低,表示实际的代码元素,例如类、接口和函数,它们构成了组件。

C4 模型有助于理解和沟通软件系统的架构,以不同的抽象级别来让开发人员、架构师和利益相关者更容易协作和讨论系统的设计。

我们将要求 ChatGPT 为我们的 ITAM 应用程序创建上下文图,包括其中包含的类。

列表 3.32,以 Mermaid 格式创建上下文图的提示
Please create a c4 context diagrams for my ITAM project, using mermaid format. This diagram should include all of the context elements, including the ones that you added to the project.

上下文图展示了系统内部和外部将发生的交互。用户将与 ITAM 系统进行交互,而 ITAM 系统将与数据库进行交互以持久化状态。接下来,上下文图说明了 ITAM 系统如何与各种接口进行交互。这些接口将公开一组 RESTful 端点,ITAM_APP 可以向其发送请求以执行各种操作,例如创建、更新、删除或获取组件详细信息。

图 3.15,ITAM 系统的上下文图, 被 ChatGPT 中断。此图应显示系统内部和外部的交互。

如果我们再往下一层,则会到达容器图。这个图会展示系统中的各个容器:用户界面、微服务等。我们将要求 ChatGPT 根据我们要求它创建上下文图时的方式来生成此图。

列表 3.33,以 Mermaid 格式创建容器图的提示
Please create a c4 container diagrams for my ITAM project, using mermaid format. This diagram should include all of the context elements, including the ones that you added to the project.

该应用程序的容器图与上下文图类似,但有一个主要区别:包括了 ITAM 用户界面。这些差异更为微妙,涉及每个层次应提供的抽象级别。上下文图是最高级别的抽象。它提供了系统的高层视图、其主要组件以及它与外部系统、API 和用户的交互方式。这有助于传达系统的边界、参与者和外部依赖性。在上下文图中,整个系统被表示为一个单一元素,重点关注其与外部世界的关系。

容器图是更深层次的抽象级别,它深入到系统的内部结构。容器图将系统分解为其主要构建块或“容器”(例如,网页应用程序、数据库、消息队列等),并展示它们之间的交互方式。它有助于理解系统的高层结构、主要使用的技术,以及容器之间的通信流程。与上下文图不同,容器图揭示了系统的内部架构,提供了关于其组件和关系的更多细节。

图 3.16 ITAM 系统的容器图,由 ChatGPT 解释。它展示了系统的组件和关系

我们将深入探索下一层:组件图。这张图将展示系统的主要组件及其相互关系。在这种情况下,组件包括控制器、服务、仓库以及外部 API。

图 3.17 ITAM 系统的组件图,由 ChatGPT 解释。它提供了 ITAM 项目内部组件及其相互作用的更详细视图

最后,代码图是最内层的同心圆。这张图几乎模仿了我们在本章早些时候制作的图表。鉴于我们是在类级别进行建模,这并不令人意外。

图 3.18 ITAM 系统的代码图。它包含了我们项目中的相关类。

我们已完成该项目的文档工作,包括一系列不断扩展的图表和一个业务需求文档。在下一章中,我们将利用这些文档构建实施,确保满足所有业务需求。

在现实世界中

一般来说,项目会从分析师创建业务需求文档开始,捕捉所有功能性和非功能性的需求。然而,鉴于我们是在一个定义良好的领域中基于一个开源项目开发此项目,我们不用担心我们的实现不能满足所有需求。

3.5 概述

  • ChatGPT 是探索业务领域周围软件生态系统的优秀工具。它允许您在不离开首选的 Web 浏览器的情况下深入研究各种实现。
  • ChatGPT 使我们能够创建诸如 Mermaid、PlantUML、经典 UML 和项目布局类图等有用的文档。
  • 六边形架构是一种旨在在应用程序的核心逻辑与其与外部系统的交互之间创建清晰分隔的架构模式,例如数据库、用户界面和第三方服务。
  • SOLID 原则是五个旨在使软件设计更灵活和可维护的软件开发设计原则。SOLID 原则包括单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖反转原则(DIP)。
  • 访问者模式是一种行为设计模式,允许您在不更改访问者操作的类的情况下在对象上定义新的操作。
  • ChatGPT 可用于为您的应用程序生成 C4 模型(上下文、容器、组件和代码)。C4 模型提供了深入系统设计的一种方式。
  • ChatGPT 是帮助项目管理文档的好工具。它可以提供完成开发的时间和材料的估计。它可以根据项目的里程碑创建一系列任务,我们可以根据这些任务跟踪开发的进度。它甚至可以创建甘特图。

第四章:使用 GitHub Copilot 构建软件

本章节涵盖的内容:

  • 使用 Copilot 开发我们系统的核心
  • 重构以应用模式
  • 集成六边形架构
  • 融合事件驱动原则

在上一章中,我们使用 ChatGPT 帮助我们设计 ITAM 系统。现在,有了设计的坚实基础,我们开始构建这个应用程序,从领域模型开始。领域模型是我们系统的核心。它代表将应用和执行我们的业务规则的类。我们将在本章节中广泛使用 GitHub Copilot。本章节最重要的收获是,通过使用大型语言模型,帮助我们阐明未知的未知,即系统中的非明显、奥秘或隐藏复杂性。它使得困难的事情变得更容易、看似不可能的事情变得可能。

还有一件事,本章节的代码占比较大。您的代码很可能与本章节中呈现的代码不完全相同。与其钻牛角尖,不如接受它,试着理解这些差异的原因。观察一下如果更改您的提示会如何修改结果,如果会,以什么方式?

4.1 奠定基础

在本章的第一部分中,我们将奠定应用程序的基础。我们从应用程序的核心开始:领域模型。领域模型应包含我们应用程序的未打磨的业务规则和责任,摆脱外部世界,专注于业务逻辑和工作流。正如您从图 4.1 中看到的那样,领域位于我们应用程序的中心。这并非巧合,因为它是应用程序的核心。在本章节中,当我们加深对六边形架构的理解和欣赏时,我们将在整章里回到这个图表。

图 4.1 传统六边形架构的可视化,其中领域或业务逻辑位于中间。

正如您从上一章中所记得的,六边形架构是一种旨在在应用程序的核心逻辑和其与外部系统的交互之间创建明确分离的架构模式。这个原则在前一张图中清晰地显示出来。

4.1.1 表达我们的领域模型

在开始之前,让我们回顾一下上一章中我们使用 ChatGPT 协助设计信息技术资产管理(ITAM)系统时创建的文档。这份文档将为我们提供模板供我们实现。正如我们现在所知道的,我们不会盲目实现 ChatGPT 提供的代码和文档的每个方面。这样做可能会意外地导致脆弱且难以更改、不安全或不完整的设计。

图 4.2 ChatGPT 为我们生成的领域对象模型,突出了类之间的关系。

如果我们深入研究上一章创建的Asset类的方法和字段,我们会注意到两件事情:首先,这与我们在第二章创建的Asset类不同。其次,ChatGPT 建议我们为这个类编写一个接受所有属性的构造函数;然而,它也添加了许多修改器方法 - “设置器” - 用于所有属性。

图 4.3 由我们自己与 ChatGPT 的帮助创建的 Asset 类。它包括一个详尽的构造函数和每个属性的修改器方法

4.1.2 更喜欢不可变性

不可变对象,其内部状态不能更改,使类更安全且更易于并发。如果两个或多个线程访问同一个对象,我们不需要同步访问;状态几乎肯定会对这两个线程都是相同的。因此,我们将更喜欢基于构造函数的值注入而不是修改器方法。当我们编写这个类时,我们将不添加修改器。

首先,我们将purchase_date字段从 Hardware 中提取出来。这个字段在所有资产之间是共享的(我们不会在本书中讨论资产被租赁的情况);因此,purchase_date字段应该属于父类。

接下来,我们将需要添加一些额外的字段,这些字段在上一章的设计中不存在:成本、useful_life和残值。这些字段将对折旧的正确计算至关重要。你可能记得折旧是一种用于在其有用寿命内分配资产成本的会计方法。你应该注意到,成本和有用寿命这两个术语出现在该定义中。因此,这些字段是必需的。此外,残值是企业、部门或公司在资产有用寿命结束时可以预期从出售资产中获得的金额。这个字段在某些折旧计算中是必要的;因此,我们将把它添加到我们的类中。

接下来,创建一个名为 domain/model 的包,添加一个名为 asset.py 的文件,然后在这个文件的开头添加以下提示。

图 4.1 让 GitHub Copilot 创建我们的Asset类的提示
# Define a class named Asset
# It should have the following attributes: 
# - id: int
# - name: str
# - status: str
# - category: str
# - cost: float
# - useful_life: float
# - salvage_value: float
# - purchase_date: datetime
# 
# The attributes cannot be None and cannot be added after construction
# However, we should be able to access the attributes using methods

当你在文件中按制表符以让 GitHub Copilot 为你自动生成代码时,你可能会注意到,尽管我们明确指示不要这样做,它仍然会创建修改器。出于这个原因以及许多其他原因(正如我们将在本章中看到的那样),在编码时你必须保持警惕和注意。当然,你可以表达你的意图,但这些工具可能会覆盖你对工具构建代码的期望。此外,你可能会注意到它使用了@dataclasses装饰器。

装饰器设计模式

装饰器设计模式是一种结构设计模式,允许你在不改变现有类的情况下动态地向对象添加新的或修改现有的行为。这是通过将现有对象包装在装饰对象中来实现的。

4.1.3 装饰我们最喜爱的类

这个装饰器会为 Python 自动生成常见的方法(例如等于,字符串表示等)。数据类是一个主要用于存储数据并且没有太多行为的类。在 Python 中,数据类是在 Python 3.7 中引入的一个实用程序,用于使主要用于存储数据的类更清洁和更有效。这与用户定义类的目的可能是混合的(即数据和行为)。在这种情况下使用数据类是有用的,因为您可以避免冗余代码来构建、表示和比较对象。

另外,对于那些在家里计分的人:我们的自动生成的代码正在自动生成代码。

列表 4.2 GitHub Copilot 响应我们的提示所创建的代码
from dataclasses import dataclass
from itam.domain.funding_details import FundingDetails
@dataclass
class Asset:
    id: int
    name: str
    status: str
    category: str
    cost: float
    useful_life: float
    salvage_value: float
    purchase_date: datetime
    def __post_init__(self):
        if self.id is None:
            raise TypeError("ID cannot be None")
        if self.name is None:
            raise TypeError("Name cannot be None")
        if self.status is None:
            raise TypeError("Status cannot be None")
        if self.category is None:
            raise TypeError("Category cannot be None")
        if self.cost is None:
            raise TypeError("Cost cannot be None")
        if self.useful_life is None:
            raise TypeError("Useful life cannot be None")
        if self.salvage_value is None:
            raise TypeError("Salvage value cannot be None")
    def get_id(self):
        return self.id
    ... # other accessors omitted for clarity

__post_init__() 方法将在构造函数之后运行,并强制确保参数中没有一个是 null(在 Python 中称为 ‘None’)。以这种方式进行防御性编码将确保在初始化后您的值不能更改。当然,您还可以添加额外的验证,比如对类别状态的可接受值。但是,如果您希望在这方面强制执行值,最好是使它们成为适当的类或使用 Enum 类。

接下来,让我们审查一下我们在上一章中创建的 Mermaid 图中的 Hardware 类。

图 4.4 我们在 ChatGPT 的帮助下起草的 Hardware 类。请注意它没有任何访问器方法。

从这个图表中可以注意到几个项目:它既没有访问器,也没有变异器方法。我们需要向我们的提示添加访问器方法。

现在我们已经审查并评估了我们在 Hardware 类中需要的方法和属性,我们应该构建它。在 domain 目录中创建一个名为 hardware.py 的文件。在此文件的开头添加列表 4.3 中的提示。

列表 4.3 一个提示,让 GitHub Copilot 创建我们的 Hardware
# Define a class called Hardware, which is an instance of Asset. 
# The Hardware entity has the following attributes:
# - serial_number: str,
# - location: Location,
# - warranty_expiration_date: date,
# - notes: str
# - maintenance_schedules: List[MaintenanceSchedule]
# - warranty: Warranty
# - retirement_date: date
# - retirement_reason: str
# - usage_statistics: UsageStatistics
# - budget: Budget
# The attributes can be None and the class should have a constructor that takes all attributes as parameters.
# The attributes should be private and the class should have accessor methods for all attributes.

出于简洁起见,列表 4.4 中不包括后构造函数代码和访问器方法。但是,它们应该会自动添加到您的文件中。

列表 4.4 Hardware 类的定义
from datetime import datetime
from dataclasses import dataclass
from itam.domain.asset import Asset
from itam.domain.location import Location
from itam.domain.warranty import Warranty
from itam.domain.maintenance_schedule import MaintenanceSchedule
from itam.domain.usage_statistics import UsageStatistics
from itam.domain.budget import Budget
@dataclass
class Hardware(Asset):
    serial_number: str
    location: Location
    warranty_expiration_date: datetime
    notes: str
    maintenance_schedules: list[MaintenanceSchedule]
    warranty: Warranty
    retirement_date: datetime
    retirement_reason: str
    usage_statistics: UsageStatistics
    budget: Budget

接下来,鉴于它在 Asset 类中被引用,让我们接下来添加 FundingDetails 类。然而,让我们先重新审查一下类图。注意到一件事,lines_of_business 字典,我们将它重命名为 department_allocations,使用字符串作为键。我们应该将其更改为实际的 Department。这是另一个提醒,我们必须积极监视生成的代码并作出如何最好地实现项目的决策。

图 4.5 我们在 ChatGPT 的帮助下起草的 FundingDetails 类。我们将希望更改业务字典的行(改名为 department_allocations)以使用 Department 作为键。

对于这个类,我们需要做两个额外的编辑。我们需要在构造函数中包含资产。这将允许我们访问进一步计算折旧所需的字段,如前面所述。另外,我们需要在构造函数中包含折旧率。这个折旧率用于递减折旧而不是直线折旧。我们可以在特定折旧构造函数或使用 mutator 方法中设置这个字段的值,但现在,我们将它添加到FundingDetails类中。包括更新后的字段名的创建FundingDetails类的更新提示列在 4.5 中。

列表 4.5 创建FundingDetails类的 GitHub Copilot 提示
# Define a class called FundingDetails
# It should have the following attributes: 
# - asset: Asset
# - depreciation_strategy: DepreciationStrategy,
# - depreciation_rate: float
# - department_allocations: Dict[Department, float]
# The attributes cannot be None and cannot be modified after construction
# However, we should be able to access the attributes using methods

生成的类中没有任何令人惊讶的内容:代码建立了内部属性,并通过构造函数进行设置。在构造函数完成后,它将检查所有属性是否为 None。

列表 4.6 FundingDetails类,省略了构造函数和访问器方法
from dataclasses import dataclass
from itam.domain.asset import Asset
from itam.domain.depreciation_strategy import DepreciationStrategy
from itam.domain.department import Department
@dataclass
class FundingDetails:
    asset: Asset
    depreciation_strategy: DepreciationStrategy
    depreciation_rate: float
    department_allocations: Dict[Department, float]
    def __post_init__(self):
        if self.asset is None:
            raise TypeError("Asset cannot be None")
        if self.depreciation_strategy is None:
            raise TypeError("Depreciation strategy cannot be None")
        if self.depreciation_rate is None:
            raise TypeError("Depreciation rate cannot be None")
        if self.department_allocations is None:
            raise TypeError("Department allocations cannot be None")
    def get_asset(self):
        return self.asset
    def get_depreciation_strategy(self):
        return self.depreciation_strategy
    def get_depreciation_rate(self):
        return self.depreciation_rate
    def get_department_allocations(self):
        return self.department_allocations

在本章中我们将生成的最后一个类是DepreciationStrategy。在上一章中,我们简要介绍了DepreciationStrategy类。当时没有提到的是,这个类将采用一种新的设计模式,策略模式。

策略设计模式

策略设计模式是一种行为设计模式,它允许您定义一组算法,将每个算法封装为一个对象,并使它们可以互换使用。策略模式的思想是为一组算法定义一个公共接口,以便它们能够互换使用,尽管可能有非常不同的实现方式。

AI 驱动的开发者(MEAP)(一)(4)https://developer.aliyun.com/article/1516313

相关文章
|
18天前
|
人工智能 数据管理 API
阿里云百炼又获大奖!阿里云百炼入选 2024 最受开发者欢迎的 AI 应用开发平台榜15强
2024年最受开发者欢迎的AI应用开发平台榜单发布,阿里云百炼入选15强。持续推动AI开发者生态建设,提供开放平台、培训支持、行业解决方案,注重数据安全与合规,致力于生态合作与共赢,加速企业数智化转型。
|
3天前
|
数据采集 人工智能 自然语言处理
Midscene.js:AI 驱动的 UI 自动化测试框架,支持自然语言交互,生成可视化报告
Midscene.js 是一款基于 AI 技术的 UI 自动化测试框架,通过自然语言交互简化测试流程,支持动作执行、数据查询和页面断言,提供可视化报告,适用于多种应用场景。
61 1
Midscene.js:AI 驱动的 UI 自动化测试框架,支持自然语言交互,生成可视化报告
|
6天前
|
机器学习/深度学习 人工智能 搜索推荐
AI在电子商务中的个性化推荐系统:驱动用户体验升级
AI在电子商务中的个性化推荐系统:驱动用户体验升级
55 17
|
2天前
|
人工智能 IDE API
AI驱动的开发者工具:打造沉浸式API集成体验
本文介绍了阿里云在过去十年中为开发者提供的API服务演变。内容分为两大部分:一是从零开始使用API的用户旅程,涵盖API的发现、调试与集成;二是回顾阿里云过去十年为开发者提供的服务及发展历程。文中详细描述了API从最初的手写SDK到自动化生成SDK的变化,以及通过API Explorer、IDE插件和AI助手等工具提升开发者体验的过程。这些工具和服务旨在帮助开发者更高效地使用API,减少配置和调试的复杂性,提供一站式的解决方案。
|
3天前
|
人工智能 安全 搜索推荐
AI 驱动研发模式升级,蓝凌软件探索效率提升之道
蓝凌软件在引入通义灵码后取得了较明显的效果。目前,蓝凌软件已使用灵码的开发人员中,周活跃用户占比超过90%、根据代码库自动生成的代码占比超33%、代码智能补全占比29%,代码注释率提升了15%,有效提升了产品代码工程化的效能。
|
17天前
|
机器学习/深度学习 人工智能 算法
转载:【AI系统】AI 发展驱动力
本文介绍了AI的起源与发展历程,强调了2016年AlphaGo胜利对AI关注度的提升。文中详细解析了AI技术在搜索引擎、图片检索、广告推荐等领域的应用,并阐述了机器学习、深度学习和神经网络之间的关系。文章还深入探讨了AI的学习方法,包括模型的输入输出确定、模型设计与开发、训练过程(前向传播、反向传播、梯度更新)及推理过程。最后,文章概述了AI算法的现状与发展趋势,以及AI系统出现的背景,包括大数据、算法进步和算力提升三大关键因素。
转载:【AI系统】AI 发展驱动力
|
29天前
|
机器学习/深度学习 人工智能 自然语言处理
AI驱动的个性化学习路径优化
在当前教育领域,个性化学习正逐渐成为一种趋势。本文探讨了如何利用人工智能技术来优化个性化学习路径,提高学习效率和质量。通过分析学生的学习行为、偏好和表现,AI可以动态调整学习内容和难度,实现真正的因材施教。文章还讨论了实施这种技术所面临的挑战和潜在的解决方案。
77 7
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
智能化软件测试:AI驱动的自动化测试策略与实践####
本文深入探讨了人工智能(AI)在软件测试领域的创新应用,通过分析AI技术如何优化测试流程、提升测试效率及质量,阐述了智能化软件测试的核心价值。文章首先概述了传统软件测试面临的挑战,随后详细介绍了AI驱动的自动化测试工具与框架,包括自然语言处理(NLP)、机器学习(ML)算法在缺陷预测、测试用例生成及自动化回归测试中的应用实例。最后,文章展望了智能化软件测试的未来发展趋势,强调了持续学习与适应能力对于保持测试策略有效性的重要性。 ####
|
1月前
|
人工智能 机器人 数据库
使用FlowiseAI轻松搭建AI驱动的交互式应用
FlowiseAI 是一款开源低代码工具,旨在帮助开发者构建自定义的语言学习模型应用。它提供拖放界面,支持与多种AI模型和数据库集成,适用于创建聊天机器人等交互式应用。使用阿里云的计算巢,用户可通过一键部署快速启动FlowiseAI,并通过简单的步骤配置和运行自定义的LLM应用。
|
1月前
|
人工智能 大数据 云计算
【AI系统】AI 发展驱动力
本文介绍了阿里云在2023年云栖大会上发布的多项新技术和产品,涵盖云计算、大数据、人工智能等领域,展示了阿里云最新的技术成果和行业解决方案,助力企业数字化转型。