主頁 > 軟體設計 > 基于ABP落地領域驅動設計-02.聚合和聚合根的最佳實踐和原則

基于ABP落地領域驅動設計-02.聚合和聚合根的最佳實踐和原則

2021-09-01 09:09:07 軟體設計

目錄
  • 系列文章
  • 前言
  • 聚合
  • 聚合和聚合根原則
    • 包含業務原則
    • 單個單元原則
    • 事務邊界原則
    • 可序列化原則
  • 聚合和聚合根最佳實踐
    • 只通過ID參考其他聚合
    • 用于 EF Core 和 關系型資料庫
    • 保持聚合根足夠小
    • 聚合根/物體中的主鍵
    • 聚合根/物體建構式
    • 業務邏輯和物體中的例外處理
    • 物體中業務邏輯需要用到外部服務
  • 學習幫助

系列文章

  • 基于ABP落地領域驅動設計-00.目錄和前言
  • 基于ABP落地領域驅動設計-01.全景圖
  • 基于ABP落地領域驅動設計-02.聚合和聚合根的最佳實踐和原則
  • 基于ABP落地領域驅動設計-03.倉儲和規約最佳實踐和原則
  • 基于ABP落地領域驅動設計-04.領域服務和應用服務的最佳實踐和原則
  • 基于ABP落地領域驅動設計-05.物體創建和更新最佳實踐
  • 基于ABP落地領域驅動設計-06.正確區分領域邏輯和應用邏輯

圍繞DDDABP Framework兩個核心技術,后面還會陸續發布核心構件實作綜合案例實作系列文章,敬請關注!
ABP Framework 研習社(QQ群:726299208)
ABP Framework 學習及實施DDD經驗分享;示例原始碼、電子書共享,歡迎加入!

前言

上一篇 基于ABP落地領域驅動設計-01.全景圖 概述了DDD理論和對應的解決方案、專案組成、專案參考關系,以及基于ABP落地DDD的通用原則,從這本篇開始,會更加深入地介紹在基于 ABP Framework 落地DDD程序中的最佳實踐和原則

圍繞DDDABP Framework兩個核心技術,后面還會陸續發布核心構件實作綜合案例實作系列文章,敬請關注!
ABP Framework 研習社(QQ群:726299208)
ABP Framework 學習及實施DDD經驗分享;示例原始碼、電子書共享,歡迎加入!

領域物件是DDD的核心,我們會依次分析聚合/聚合根、倉儲、規約、領域服務的最佳實踐和規則,內容較多,會拆分成多個章節單獨展開,

本文重點討論領域物件——聚合和聚合根的最佳實踐和原則

首先我們需要一個業務場景,例子中會用到 GitHub 的一些概念,如:Issue(建議)、Repository(代碼倉庫)、Label(標簽)和User(用戶),

下圖顯示了業務場景對應的聚合、聚合根、物體、值物件以及它們之間的關系,

image

Issue 聚合是由 Issue(聚合根)、Comment(物體)和 IssuelLabel(值物件)組成的集合,因為其他聚合相對簡單,所以我們重點分析 Issue 聚合

image

聚合

正如前面所講,一個聚合是一系列物件(物體和值物件)的集合,通過聚合根將所有關聯物件系結在一起,本節將介紹與聚合相關的最佳實踐和原則,

我們對聚合根子集合物體都使用物體這個術語,除非明確寫出聚合根或子集合物體,

聚合和聚合根原則

包含業務原則

  • 物體負責實作與其自身屬性相關的業務規則,
  • 聚合根還負責其子集合物體狀態管理,
  • 聚合應該通過實作領域規則規約來保持自身的完整性有效性,這意味著,與資料傳輸物件(DTO)不同,物體具有實作業務邏輯的方法,實際上,我們應該盡可能在物體中實作業務規則

單個單元原則

聚合及其所有子集合,作為單個單元被檢索和保存,例如:如果向 Issue 添加 Comment,需要這樣做:

  • 從資料庫中獲取 Issue 包含所有子集合:Comments (該問題的評論串列) 和 IssueLabels (該問題的標簽集合),
  • Issue 類中呼叫方法添加一個新的 Comment,比如: Issue.AddCommnet(...)
  • 作為一個單一的資料庫更新操作,將 Issue(包括所有子集合)保存到資料庫,

對于習慣使用 EF Core 和 關系資料的開發者來說,這看起來似乎有些奇怪,獲取 Issue 的所有資料是沒有必要低效的,為什么我們不直接執行一個SQL插入命令到資料庫,而不查詢任何資料呢?

答案是,我們應該在代碼中實作業務規則并保持資料的一致性和完整性,如果我們有一個業務規則,如:用戶不能對鎖定的 Issue 進行評論,我們如何不通過檢索資料庫中資料的情況下,檢查 Issue 的鎖定狀態呢?所以,只有當應用程式代碼中的相關物件可用時,即獲取到聚合及其所有子集合資料時,我們才能執行該業務規則,

另一方面,MongoDB開發者會發現這個規則非常自然,因為在 MongoDB 中,一個聚合物件(包括子集合)被保存在資料庫中的一個集合中,而在關系型資料庫中,它被分布在資料庫中幾個表中,因此,當你得到一個聚合時,所有的子集合已經作為查詢的一部分被檢索出來了,不需要任何額外配置,

ABP框架有助于在您的應用程式中實作這一原則,

示例:添加 Comment 到 Issue

public class IssueAppService : ApplicationService ,IIssueAppService
{
  private readonly IRepository<Issue,Guid> _issueRepository;
  public IssueAppService(IRepository<Issue,Guid> issueRepository)
  {
    _issueRepository = issueRepository;
  }
  [Authorize]
  public async Task CreateCommentAsync(CreateCommentDto input)
  {
    var issue = await _issueRepository.GetAsync(input.IssueId);
    issue.AddComment(CurrentUser.GetId(),input.Text);
    await _issueRepository.UpdateAsynce(issue);
  }
}

_issueRepository.GetAsync(...)方法默認作為單個單元檢索 Issue 物件并包含所有子集合,對于 MongoDB 來說這個操作開箱即用,但是使用 EF Core 需要配置聚合與資料庫映射,配置后 EF Core 倉儲實作 會自動處理,_issueRepository.GetAsync(...)方法提供一個可選引數includeDetails,可以傳遞值 false 禁用該行為,不包含子集合物件,只在需要時啟用它,

Issue.AddComment(...)傳遞引數 userIdtext ,表示用戶ID評論內容,添加到 IssueComments 集合中,并實作必要的業務邏輯驗證,

最后,使用 _issueRepository.UpdateAsync(...) 保存更改到資料庫,

EF Core 提供 變更跟蹤(Change Tracking)功能,實際上你不需要呼叫 _issueRepository.UpdateAsync(...) 方法,會自動進行保存,這個功能是由 ABP 作業單元系統 提供,應用服務的方法作為一個單獨的作業單元,在執行完之后會自動呼叫 DbContext.SaveChanges(),當然,如果使用 MongoDB 資料庫,則需要顯示地更新已經更改的物體,
所以,如果你想要撰寫獨立于資料庫提供程式的代碼,應該總是為要更改的物體呼叫UpdateAsync()方法,

事務邊界原則

一個聚合通常被認為是一個事務邊界,如果用例使用單個聚合,讀取并保存為單個單元,那么對聚合物件所做的所有更改,將作為原子操作保存,而不需要顯式地使用資料庫事務,

當然,我們可能需要處理將多個聚合實體作為單一用例更改的場景,此時需要使用資料庫事務確保更新操作的原子性資料一致性,正因為如此,ABP框架為一個用例(即一個應用程式服務方法)顯式地使用資料庫事務,一個應用程式服務方法,就是一個作業單元,

可序列化原則

聚合(包含根物體和子集合)應該是可序列化的,并且可以作為單個單元在網路上進行傳輸,舉個例子,MongoDB序列化聚合為Json檔案保存到資料庫,反序列化從資料庫中讀取的Json資料,

當您使用關系資料庫和ORM時,沒有必要這樣做,然而,它是領域驅動設計的一個重要實踐,

聚合和聚合根最佳實踐

以下最佳實踐確保實作上述原則,

只通過ID參考其他聚合

一個聚合應該只通過其他聚合的ID參考聚合,這意味著你不能添加導航屬性到其他聚合,

  • 這條規則使得實作可序列化原則得以實作,
  • 可以防止不同聚合相互操作,以及將聚合的業務邏輯泄露給另一個聚合,

我們來看一個例子,兩個聚合根:GitRepositoryIssue

public class GitRepository:AggregateRoot<Guid>
{
  public string Name {get;set;}
  public int StarCount{get;set;}
  public Collection<Issue> Issues {get;set;} //錯誤代碼示例
}

public class Issue:AggregateRoot<Guid>
{
  public tring Text{get;set;}
  public GitRepository Repository{get;set;} //錯誤代碼示例
  public Guid RepositoryId{get;set;} //正確示例
}
  • GitRepository 不應該包含 Issue 集合,他們是不同聚合,
  • Issue 不應該設定導航屬性關聯 GitRepository ,因為他們是不同聚合,
  • Issue 使用 RepositoryId 關聯 Repository 聚合,正確,

當你有一個 Issue 需要關聯的 GitRepository 時,那么可以從資料庫通過 RepositoryId 直接查詢,

用于 EF Core 和 關系型資料庫

在 MongoDB 中,自然不適合有這樣的導航屬性/集合,如果這樣做,在源集合的資料庫集合中會保存目標集合物件的副本,因為它在保存時被序列化為JSON,這樣可能會導致持久化資料的不一致,

然而,EF Core 和關系型資料庫的開發者可能會發現這個限制性的規則是不必要的,因為 EF Core 可以在資料庫的讀寫中處理它,

但是我們認為這是一條重要的規則,有助于降低領域的復雜性防止潛在的問題,我們強烈建議實施這條規則,然而,如果你認為忽略這條規則是切實可行的,請參閱前面基于ABP落地領域驅動設計-01.全景圖中關于資料庫獨立性原則的討論部分,

保持聚合根足夠小

一個好的做法是保持一個簡單而小的聚合,這是因為一個聚合體將作為一個單元被加載和保存,讀/寫一個大物件會導致性能問題,

請看下面的例子:

public class UserRole:ValueObject
{
  public Guid UserId{get;set;}
  public Guid RoleId{get;set;}
}

public class Role:AggregateRoot<Guid>
{
  public string Name{get;set;}
  public Collection<UserRole> Users{get;set;} //錯誤示例:角色對應的用戶是不斷增加的
}
public class User:AggregateRoot<Guid>
{
  public string Name{get;set;}
  public Collection<UserRole> Roles{get;set;}//正確示例:一個用戶擁有的角色數量是有限的
}

Role聚合 包含 UserRole 值物件集合,用于跟蹤分配給此角色的用戶,注意,UserRole 不是另一個聚合,對于規則僅通過Id參考其他聚合沒有沖突,

然而,實際卻存在一個問題,在現實生活中,一個角色可能被分配給數以千計(甚至數以百萬計)的用戶,每當你從資料庫中查詢一個角色時,加載數以千計的資料項是一個重大的性能問題,記住:聚合是由它們的子集合作為一個單一單元加載的

另一方面,用戶可能有角色集合,因為實際情況中用戶擁有的角色數量是有限的,不會太多,當您使用用戶聚合時,擁有一個角色串列可能會很有用,且不會影響性能,

如果你仔細想想,當使用非關系型資料庫(如MongoDB)時,當RoleUser都有關系串列時還有一個問題:在這種情況下,相同的資訊會在不同的集合中重復出現,將很難保持資料的一致性,每當你在User.Roles中添加一個項,你也需要將它添加到Role.Users中,

因此,根據以下因素來確定聚合邊界和大小:

  • 考慮物件關聯性,是否需要在一起使用,
  • 考慮性能,查詢(加載/保存)性能和記憶體消耗,
  • 考慮資料的完整性、有效性和一致性,

而實際:

  • 大多數聚合根沒有子集合,
  • 一個子集合最多不應該包含超過100-150個條目,如果您認為集合可能有更多項時,請不要定義集合作為聚合的一部分,應該考慮為集合內的物體提取為另一個聚合根,

聚合根/物體中的主鍵

  • 一個聚合根通常有一個ID屬性作為其識別符號(主鍵,Primark Key: PK),推薦使用 Guid 作為聚合根物體的PK,
  • 聚合中的物體(不是聚合根)可以使用復合主鍵

示例:聚合根和物體

//聚合根:單個主鍵
public class Organization
{
  public Guid Id{get;set;}
  public string Name{get;set;}
  //...
}
//物體:復合主鍵
public class OrganizationUser
{
  public Guid OrganizationId{get;set;} //主鍵
  public Guid UserId{get;set;}//主鍵
  public bool IsOwner{get;set;}
  //...
}
  • Organization 包含 Guid 型別主鍵 Id
  • OrganizationUserOrganization 中的子集合,有復合主鍵:OrganizationIdUserId

這并不意味著子集合物體應該總是有復合主鍵,只有當需要時設定;通常是單一的ID屬性,

復合主鍵實際上是關系型資料庫的一個概念,因為子集合物體有自己的表,需要一個主鍵,另一方面,例如:在MongoDB中,你根本不需要為子集合物體定義主鍵,因為它們是作為聚合根的一部分來存盤的,

聚合根/物體建構式

建構式是物體的生命周期開始的地方,一個設計良好的建構式,擔負以下職責:

  • 獲取所需的物體屬性引數,來創建一個有效的物體,應該強制只傳遞必要的引數,并可以將非必要的屬性作為可選引數
  • 檢查引數的有效性,
  • 初始化子集合,

示例:Issue(聚合根)建構式

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Volo.Abp;
using Volo.Abp.Domain.Entities;

namespace IssueTracking.Issues
{
  public class Issue:AggregateRoot<Guid>
  {
    public Guid RepositoryId{get;set;}
    public string Title{get;set;}
    public string Text{get;set;}
    public Guid? AssignedUserId{get;set;}
    public bool IsClosed{get;set;}
    pulic IssueCloseReason? CloseReason{get;set;} //列舉
    public ICollection<IssueLabel> Labels {get;set;}

    public Issue(
      Guid id,
      Guid repositoryId,
      string title,
      string text=null,
      Guid? assignedUserId = null
    ):base(id)
    {
      //屬性賦值
      RepositoryId=repositoryId;
      //有效性檢測
      Title=Check.NotNullOrWhiteSpace(title,nameof(title));

      Text=text;
      AssignedUserId=assignedUserId;
      //子集合初始化
      Labels=new Collection<IssueLabel>();
    }
    private Issue(){/*反序列化或ORM 需要*/}
  }
}
  • Issue類通過其建構式引數,獲得屬性所需的值,以此創建一個正確有效的物體,
  • 在建構式中驗證輸入引數的有效性,比如:Check.NotNullOrWhiteSpace(...) 當傳遞的值為空時,拋出例外ArgumentException
  • 初始化子集合,當使用 Labels 集合時,不會獲取到空參考例外,
  • 建構式將引數id傳遞給base類,不在建構式中生成 Guid,可以將其委托給另一個 Guid生成服務,作為引數傳遞進來,
  • 無參建構式對于ORM是必要的,我們將其設定為私有,以防止在代碼中意外地使用它,

物體屬性訪問器和方法

上面的示例代碼,看起來可能很奇怪,比如:在建構式中,我們強制傳遞一個不為nullTitle,但是,我們可以將 Title 屬性設定為 null,而對其沒有進行任何有效性控制,這是因為示例代碼關注點暫時只在建構式,

如果我們用 public 設定器宣告所有的屬性,就像上面的Issue類中的屬性例子,我們就不能在物體的生命周期中強制保持其有效性和完整性,所以:

  • 當需要在設定屬性時,執行任何邏輯,請將屬性設定為私有private
  • 定義公共方法來操作這些屬性,

示例:通過方法修改屬性

namespace IssueTracking.Issues
{
  public Guid RepositoryId {get; private set;} //不更改
  public string Title { get; private set; } //更改,需要非空驗證
  public string Text{get;set;} //無需驗證
  public Guid? AssignedUserId{get;set;} //無需驗證
  public bool IsClosed { get; private set; } //需要和 CloseReason 一起更改
  public IssueCloseReason? CloseReason { get;private set;} //需要和 IsClosed 一起更改

  public class Issue:AggregateRoot<Guid>
  {
    //...
    public void SetTitle(string title)
    {
      Title=Check.NotNullOrWhiteSpace(title,nameof(title));
    }

    public void Close(IssueCloseReason reason)
    {
      IsClosed = true;
      CloseReason =reason;
    }

    public void ReOpen()
    {
      IsClosed=false;
      CloseReason=null;
    }
  }
}
  • RepositoryId 設定器設定為私有private,因為 Issue 不能將 Issue 移動到另一個 Repository 中,該屬性創建之后無需更改,
  • Title 設定器設定為私有,當需要更改時,可以使用 SetTitle 方法,這是一種可控的方式,
  • TextAssignedUserId 都有公共設定器,因為這兩個欄位并沒有約束,可以是null或任何值,我們認為沒有必要定義單獨的方法來設定它們,如果以后需要,可以添加更改方法并將其設定器設定為私有,領域層是內部專案,并不會暴露給客戶端使用,所以這種更改不會有問題
  • IsClosedIssueCloseReason 是成對修改的屬性,分別定義 CloseReOpen 方法一起修改他們,通過這種方式,可以防止在沒有任何理由的情況下關閉一個問題,

業務邏輯和物體中的例外處理

當你在物體中進行驗證和實作業務邏輯,經常需要管理例外:

  • 創建特定領域例外,
  • 必要時在物體方法中拋出這些例外,

示例:

public class Issue:AggregateRoot<Guid>
{
  //..
  public bool IsLocked {get;private set;}
  public bool IsClosed{get;private set;}
  public IssueCloseReason? CloseReason {get;private set;}

  public void Close(IssueCloseReason reason)
  {
    IsClose = true;
    CloseReason =reason;
  }
  public void ReOpen()
  {
    if(IsLocked)
    {
      throw new IssueStateException("不能打開一個鎖定的問題!請先解鎖!");
    }
    IsClosed=false;
    CloseReason=null;
  }
  public void Lock()
  {
    if(!IsClosed)
    {
      throw new IssueStateException("不能鎖定一個關閉的問題!請先打開!");
    }
  }
  public void Unlock()
  {
    IsLocked = false;
  }
}

這里有兩個業務規則:

  • 鎖定的Issue不能重新打開
  • 不能鎖定一個關閉的Issue

Issue 類在這些業務規則中拋出例外 IssueStateException

namespace IssueTracking.Issues
{
  public class IssueStateException : Exception
  {
    public IssueStateException(string message)
      :base(message)
      {

      }
  }
}

拋出此類例外有兩個潛在問題:

  1. 在這種例外情況下,終端用戶是否應該看到例外(錯誤)訊息?如果是,如何實作本地化例外訊息?因為不能在物體中注入和使用IStringLocalizer,導致不能使用本地化系統,
  2. 對于 Web 應用程式或 HTTP API,應該給客戶端回傳什么 HTTP Status Code?

ABP框架 Exception Handing 系統處理了這些問題,

示例:拋出業務例外

using Volo.Abp;
namespace IssuTracking.Issues
{
  public class IssueStateException : BuisinessException
  {
    public IssueStateExcetipn(string code)
      : base(code)
      {

      }
  }
}
  • IssueStateException 類繼承 BusinessException 類,ABP框架在請求禁用時默認回傳 403 HTTP 狀態碼;發生內部錯誤是回傳 500 HTTP 狀態碼,
  • code 用作本地化資源檔案中的一個,用于查找本地化訊息,

現在,我們可以修改 ReOpen 方法:

public void ReOpen()
{
  if(IsLocked)
  {
    throw new IssueStateException("IssueTracking:CanNotOpenLockedIssue");
  }
  IsClosed=false;
  CloseReason=null;
}

建議:使用常量代替魔術字串"IssueTracking:CanNotOpenLockedIssue"

然后在本地化資源中添加一個條目,如下所示:

"IssueTracking:CanNotOpenLockedIssue":"不能打開一個鎖定的問題!請先解鎖!"
  • 當拋出例外時,ABP自動使用這個本地化訊息(基于當前語言)向終端用戶顯示,
  • 例外Code("IssueTracking:CanNotOpenLockedIssue")被發送到客戶端,因此它可以以編程方式處理錯誤情況,

物體中業務邏輯需要用到外部服務

當業務邏輯只使用該物體的屬性時,在物體方法中實作業務規則是很簡單的,如果業務邏輯需要查詢資料庫或使用任何應該從依賴注入系統中獲取的外部服務時,該怎么辦?請記住,物體不能注入服務

有兩個方式實作:

  • 在物體方法上實作業務邏輯,并將外部依賴項作為方法的引數,
  • 創建領域服務(Domain Service)

領域服務在后面介紹,現在讓我們看看如何在物體類中實作它,

示例:業務規則:一個用戶不能同時分配超過3個未解決的問題

public class Issue:AggregateRoot<Guid>
{
  //..
  public Guid? AssignedUserId{get;private set;}
  //問題分配方法
  public async Task AssignToAsync(AppUser user,IUserIssueService userIssueService)
  {
    var openIssueCount = await userIssueService.GetOpenIssueCountAsync(user.Id);
    if(openIssueCount >=3 )
    {
      throw new BusinessException("IssueTracking:CanNotOpenLockedIssue");
    }
    AssignedUserId=user.Id;
  }
  public void CleanAssignment()
  {
    AssignedUserId=null;
  }
}
  • AssignedUserId 屬性設定器設定為私有,通過 AssignToAsyncCleanAssignment 方法進行修改,
  • AssignToAsync 獲取一個 AppUser 物體,實際上只用到 user.Id,傳遞物體是為了確保引數值是一個存在的用戶,而不是一個隨機值,
  • IUserIssueService 是一個任意的服務,用于獲取分配給用戶的問題數量,如果業務規則不滿足,則拋出例外,所有規則滿足,則設定 AssignedUserId 屬性值,

此方法完全實作了應用業務邏輯,然而,它有一些問題:

  • 物體變得復雜,因為物體類依賴外部服務,
  • 物體變得難用,呼叫方法時需要注入依賴的外部服務 IUserIssueService 作為引數,

聚合和聚合根的最佳實踐和原則部分完結!

學習幫助

圍繞DDDABP Framework兩個核心技術,后面還會陸續發布核心構件實作綜合案例實作系列文章,敬請關注!

ABP Framework 研習社(QQ群:726299208)
專注 ABP Framework 學習及DDD實施經驗分享;示例原始碼、電子書共享,歡迎加入!
image

記錄技術修行中的反思與感悟,以碼傳心,以軟制道,知行合一!

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/296419.html

標籤:領域驅動設計

上一篇:物聯網的應用模式

下一篇:為什么說開源的COLA既是架構也是框架?

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more