Placeholder Image

字幕列表 影片播放

由 AI 自動生成
  • In the first part, you learned the core concepts of clean architecture as it pertains to Flutter.

    在第一部分中,您學習了與 Flutter 相關的簡潔架構的核心概念。

  • We also created a bunch of empty folders, which you can see on the side right now, for presentation, domain, and data.

    我們還創建了一堆空文件夾,你現在可以在邊上看到,分別用於展示、域和數據。

  • This is for the Number Trivia app we are building.

    這是我們正在開發的數字瑣事應用程序。

  • And now it's time to start filling in these empty folders with actual code, of course, using test-driven development.

    現在是時候開始用實際代碼填充這些空文件夾了,當然要使用測試驅動開發。

  • Hello, welcome to ResoCoder, where you are getting prepared for real app development by building better, faster, more stable apps.

    您好,歡迎來到 ResoCoder,在這裡,您將通過構建更好、更快、更穩定的應用程序,為真正的應用程序開發做好準備。

  • So subscribe and hit the bell if you want to grow your coding skills.

    如果你想提高編碼技能,請訂閱並按鈴。

  • So, of course, whenever you are building an app, you should start with the UI and user experience first, but I've done that homework for you.

    當然,無論何時創建應用程序,您都應該首先從用戶界面和用戶體驗入手,但我已經為您做了這方面的功課。

  • As you've seen in the previous tutorial, the app looks something like this.

    正如您在前面的教程中所看到的,應用程序看起來是這樣的。

  • The app looks like this, the actual coding process, though, will happen from the innermost stable layers and will progress toward the outer layers.

    應用程序看起來是這樣的,但實際的編碼過程將從最內層的穩定層開始,然後向外層推進。

  • So in the case of clean architecture, as it was proposed by Uncle Bob, we can see that we should start from the domain layer, particularly from entities.

    是以,就鮑勃大叔提出的 "簡潔架構 "而言,我們可以看到,我們應該從領域層入手,尤其是從實體入手。

  • So we are going to create an entity right now.

    是以,我們現在要創建一個實體。

  • Before we do that, though, before we can create an entity for the number trivia, we have to add some packages over to pubspec.yaml, because we are going to be using quite a lot of packages to make things easier for us, of course.

    不過,在此之前,在為數字瑣事創建實體之前,我們必須在 pubspec.yaml 中添加一些套裝軟體,因為我們將使用大量套裝軟體來簡化工作。

  • And with that, I have to tell you to make sure you check out the written tutorial from the link in the description, where you can find all of the code written in this video, all of the links, and all of that good stuff.

    是以,我必須告訴大家,請務必通過說明中的鏈接查看書面教程,在那裡你可以找到本視頻中編寫的所有代碼、所有鏈接以及所有好東西。

  • And go through this lesson at your own pace.

    請按照自己的節奏來學習本課。

  • This pubspec.yaml file is one of the few things which I'm going to just copy and paste over on video so that I don't bore you to death.

    這個 pubspec.yaml 文件是我要在視頻中複製粘貼的少數幾個文件之一,這樣就不會讓你覺得無聊了。

  • So let's just delete all of these unnecessary comments.

    所以,還是把這些不必要的評論都刪掉吧。

  • We'll just clutter up the pubspec.yaml file.

    我們只會讓 pubspec.yaml 文件變得雜亂無章。

  • And then the dependencies and dev dependencies will be the following.

    然後,依賴項和開發依賴項如下。

  • So we have getit, which is the service locator.

    我們有 getit,它是服務定位器。

  • I've added all of the dependencies right now so that we later do not have to come back to this yaml file and just lose time on that.

    我現在已經添加了所有的依賴項,這樣我們以後就不必再回到這個 yaml 文件,以免浪費時間。

  • So we have getit.

    所以我們得到了它。

  • Then we have flutter block for state management, equatable for value equality.

    然後,我們用 flutter 塊來進行狀態管理,用 equatable 來實現值相等。

  • Then we are going to touch on some functional programming concepts.

    然後,我們將學習一些函數式編程概念。

  • Then for remote API, we have connectivity and HTTP packages, of course.

    當然,對於遠程 API,我們還有連接和 HTTP 包。

  • And for local cache, we are going to be using just simple shared preferences.

    對於在地緩存,我們將使用簡單的共享首選項。

  • As for dev dependencies, these are the ones which do not get packaged together with your app once you build it.

    至於開發依賴項,這些依賴項在您構建應用程序時不會與應用程序打包在一起。

  • We have flutter test.

    我們進行了飄動測試。

  • This was here even before I think by default.

    我想,這在默認情況下就已經存在了。

  • And then we have Mockito for creating mocks.

    此外,我們還有用於創建模擬的 Mockito。

  • We are going to get to all of this as we progress forward with this tutorial.

    隨著本教程的深入,我們將瞭解所有這些內容。

  • But just so that you have all of the packages already present, we've added them over here.

    不過,為了讓大家能看到所有的套裝軟體,我們已經把它們添加到了這裡。

  • And I'm just going to delete all of the other comments so that they don't clutter up pubspec.

    我將刪除所有其他評論,以免它們擾亂 Pubspec。

  • And here we go.

    我們開始吧。

  • The pubspec.yaml file is completely finished.

    pubspec.yaml 文件已全部完成。

  • All right.

    好的

  • So let's now move on to creating the actual entity.

    現在,讓我們開始創建實際的實體。

  • The first question we have to ask ourselves is what kind of data will the NumberTrivia operate with?

    我們要問自己的第一個問題是,NumberTrivia 將使用什麼樣的數據?

  • Well, of course, with NumberTrivia entities.

    當然,有了 NumberTrivia 實體。

  • So we can create a new file under the domain entities.

    是以,我們可以在域實體下創建一個新文件。

  • Let's call it NumberTrivia.dart, of course.

    當然,我們就叫它 NumberTrivia.dart。

  • And now we have to take a look at the JSON response from the API because based on that response, we are going to model our class here.

    現在,我們必須查看一下來自 API 的 JSON 響應,因為根據該響應,我們將在這裡為我們的類建模。

  • So if you go to numbersapi.com forward slash 42 or some other number and then question mark JSON, so it's a query parameter, you will obtain the following response.

    是以,如果您訪問 numbersapi.com 的正斜槓 42 或其他數字,然後用問號標記 JSON,將其作為一個查詢參數,您將得到以下響應。

  • And of course, this link is also available from the written tutorial.

    當然,這個鏈接也可以從書面教程中獲取。

  • So this is the response which we will be operating with throughout our app.

    是以,這就是我們將在整個應用程序中使用的響應。

  • And we need to model our NumberTrivia entity so that it can contain this kind of data.

    我們需要為 NumberTrivia 實體建模,使其能夠包含此類數據。

  • So what do we actually really need from this JSON object?

    那麼,我們到底需要這個 JSON 對象做什麼呢?

  • Well, we need the text, obviously, because that's the core, so to say, functionality of our NumberTrivia app.

    顯然,我們需要文本,因為這是我們的 NumberTrivia 應用程序的核心功能。

  • We are going to store the number, but then we have two interesting fields, which are found and type.

    我們要存儲的是數字,但還有兩個有趣的字段,分別是 "找到 "和 "類型"。

  • We are not going to need those at all because found is completely irrelevant for our case, and the type will be always trivia.

    我們根本不需要這些,因為 "發現 "與我們的情況完全無關,而 "類型 "永遠是瑣事。

  • And why is found completely irrelevant?

    為什麼發現完全無關緊要?

  • Well, if I search for a number other than 42, so for example, some complete gibberish long number, which doesn't have any entry in the numbers API and search for that, you can see that found is false, but we still obtain some kind of a reasonable response that this is an uninteresting number.

    好吧,如果我搜索一個 42 以外的數字,例如,一個完全胡言亂語的長數字,在數字應用程序接口中沒有任何條目,然後搜索這個數字,你可以看到發現是假的,但我們仍然得到了某種合理的迴應,即這是一個無趣的數字。

  • So we can still display even this message to the user in the app.

    是以,我們仍然可以在應用程序中向用戶顯示這條資訊。

  • We do not need to worry at all whether or not the number was found.

    我們完全不必擔心號碼是否被找到。

  • So the bottom line is that we need the text and number fields to map to properties in our app.

    是以,我們需要將文本和數字字段映射到應用程序中的屬性。

  • So let's create a class, NumberTrivia, which will extend Equatable.

    是以,讓我們創建一個名為 NumberTrivia 的類,它將擴展 Equatable。

  • And let's import Equatable here.

    讓我們在這裡導入 Equatable。

  • This is so that value equality is made simple because by default, Dart supports only referential equality.

    這樣做的目的是使值相等變得簡單,因為默認情況下,Dart 只支持引用相等。

  • So even two objects which contain completely the same data will not be equal unless they are referencing to the same point in memory.

    是以,即使是包含完全相同數據的兩個對象也不會相等,除非它們引用的是內存中的相同點。

  • With Equatable, this is completely changed.

    有了 Equatable,情況就完全不同了。

  • When two objects contain the same values, the same data, they will be equal.

    當兩個對象包含相同的值、相同的數據時,它們就會相等。

  • And we do not need to override equal operator or hash code, anything like that.

    我們不需要覆蓋等號運算符或哈希代碼之類的東西。

  • Equatable does that for us.

    Equatable 為我們做到了這一點。

  • So it saves us a bunch of boilerplate.

    這樣就省去了一大堆模板。

  • And now we need to store the text and number.

    現在我們需要存儲文本和數字。

  • So final string text, and then final int number.

    所以是最終字符串文本,然後是最終 int 數字。

  • And of course, we need to put them into constructor.

    當然,我們需要將它們放入構造函數中。

  • So we can just hit control dot, at least in VS code, and create constructor for all of these fields here.

    是以,至少在 VS 代碼中,我們可以直接點擊控制點,為所有這些字段創建構造函數。

  • But we are not just going to have this kind of a constructor with unnamed arguments.

    但我們並不打算只使用這種帶有未命名參數的構造函數。

  • We want to have them named.

    我們希望為他們命名。

  • And of course, we are going to make them required.

    當然,我們也會把它們變成必需品。

  • So we need to import meta package.

    是以,我們需要導入元套裝軟體。

  • So meta dot Dart.

    所以元點飛鏢。

  • And let's just make these required.

    讓我們把這些變成必需品吧。

  • And also this one will be required here.

    這裡也需要這個。

  • And we mustn't forget to pass these over to the super constructor, because Equatable needs to know which properties to make equality happen on.

    我們不能忘記將這些屬性傳遞給超級構造函數,因為 Equatable 需要知道在哪些屬性上實現平等。

  • So we are going to pass them in as a list.

    是以,我們要將它們作為一個列表傳入。

  • So we pass in text and also number to the super constructor, which is Equatable.

    是以,我們將文本和數字傳遞給超級構造函數,也就是 Equatable。

  • And you may have noticed that we didn't do any test driven development here.

    你可能已經注意到,我們在這裡沒有進行任何測試驅動開發。

  • That's because this class is really, it doesn't have any logic.

    這是因為這個類實在是沒有任何邏輯可言。

  • There is why we aren't writing any tests for this number trivia entity.

    這就是為什麼我們沒有為這個數字瑣事實體編寫任何測試的原因。

  • Should you have any logic inside your entity, you would write test even for entity, but that's not the case with this one.

    如果您的實體內有任何邏輯,您甚至會為實體編寫測試,但這個實體並非如此。

  • All right, now let's talk about use cases.

    好了,現在我們來談談使用案例。

  • They are where the business logic gets executed.

    它們是執行業務邏輯的地方。

  • And sure, there won't be much logic in the number trivia app, because all a use case will do is to get data from a repository.

    當然,數字瑣事應用程序中不會有太多邏輯,因為用例所做的只是從存儲庫中獲取數據。

  • So just so you can see that, here we go with the diagram.

    所以,為了讓你看清楚,我們在這裡用圖表來說明。

  • We have use case here.

    我們這裡有使用案例。

  • It will get data from the repository and they will communicate together through these entities.

    它將從存儲庫中獲取數據,並通過這些實體進行通信。

  • And we have created just one such entity called number trivia a few moments ago.

    剛才我們就創建了一個這樣的實體,叫做數字瑣事。

  • And we are going to have two use cases in our finished app, getConcreteNumberTrivia and getRandomNumberTrivia.

    在我們完成的應用程序中,將有兩個用例:getConcreteNumberTrivia 和 getRandomNumberTrivia。

  • By looking at this diagram, as I've already said, you know that use cases get data from repositories, and then they pass that data, which in this case are entities, over to the presentation logic holders and to the whole presentation layer.

    通過觀察這個圖表,正如我已經說過的,你會知道用例從存儲庫中獲取數據,然後將這些數據(在本例中是實體)傳遞給呈現邏輯持有者和整個呈現層。

  • So it kind of makes sense that when a use case is communicated with repositories, the repository should return an entity, right?

    是以,當用例與資源庫通信時,資源庫應該返回一個實體,這也是合情合理的,對嗎?

  • But because we have to allow for asynchronous operations, we are going to wrap this entity, which in this case is number trivia, into a future.

    但是,由於我們必須允許異步操作,所以我們要把這個實體(在本例中是數字瑣事)包裝成一個未來。

  • But that's not the actual case of what we are going to do.

    但這並不是我們要做的實際情況。

  • We have to think about error handling as well, because is it the best choice to let exceptions freely propagate?

    我們還必須考慮錯誤處理,因為讓異常自由傳播是否是最好的選擇?

  • Having to remember, and when you have to remember something, you know that that's not cool.

    必須記住,而當你必須記住一些東西時,你就會知道那並不酷。

  • We have to remember to cache them somewhere else in the code if we let exceptions just nicely propagate at their own peril.

    如果我們任由異常自顧自地傳播,就必須記住在代碼的其他地方緩存它們。

  • I do not think that this is the best choice.

    我認為這不是最佳選擇。

  • Instead, we want to catch exceptions as early as possible inside the repository here, and then return failure objects from the repository methods.

    相反,我們希望儘早在版本庫中捕獲異常,然後從版本庫方法中返回失敗對象。

  • So unlike exceptions, you will not have to try and catch all of these exceptions.

    是以,與異常不同,您不必嘗試捕獲所有這些異常。

  • You will have failure objects, which are returned as regular objects, return types from methods.

    您將從方法中獲得失敗對象(作為普通對象返回)和返回類型。

  • There will be no special error flow, nothing like that.

    不會有特殊的錯誤流程,不會有類似的情況。

  • It will be just like a regular data flow without any kind of try-catch-finally blocks in the rest of the app from the repository upwards.

    這就像一個普通的數據流,從存儲庫開始,應用程序的其他部分不會出現任何 try-catch-finally 塊。

  • So to recap, repository and then subsequently use cases will return both number trivia objects, and also they will return failure objects from their methods.

    是以,概括地說,存儲庫和隨後的用例既會返回數字瑣事對象,也會從其方法中返回失敗對象。

  • So how is something like that possible?

    那麼,這樣的事情怎麼可能發生呢?

  • How can we return number trivia or failure from the same method?

    我們如何從同一個方法中返回數字瑣事或失敗?

  • Welcome functional programming, and that's why we have imported the darts package over to our app.

    歡迎使用功能編程,這也是我們將飛鏢套裝軟體導入應用程序的原因。

  • This functional programming darts package allows us to simply use an either type.

    這個函數式編程飛鏢套裝軟體允許我們簡單地使用任一類型。

  • While I will not pretend that I am some functional programming pro, at least not yet, you don't need to know any really advanced stuff about either type, which is a functional programming concept, will allow us to return either number trivia or failure, which we are going to create next.

    雖然我不會假裝自己是函數式編程專家,至少現在還不是,但你不需要知道任何關於這兩種類型的高級知識,這是函數式編程的一個概念,它將允許我們返回數字瑣事或失敗,接下來我們將創建這兩種類型。

  • Obviously, we still do not have any failure classes.

    顯然,我們仍然沒有任何不及格的班級。

  • So let's define them right now.

    那麼我們現在就來定義它們。

  • We are going to create an error subfolder under core, and this will hold failures.dart.

    我們將在 core 下創建一個錯誤子文件夾,其中將存放 failures.dart。

  • And the failure here will be really only a simple class.

    而這裡的失敗其實只是一個簡單的類。

  • It will be abstract actually, because then later on, we are going to create some concrete implementations of failure objects.

    實際上,它將是抽象的,因為稍後我們將創建一些故障對象的具體實現。

  • And again, we are not writing this in test-driven development manner, because there is nothing to test in an abstract class.

    再說一遍,我們並不是以測試驅動開發的方式編寫的,因為抽象類中沒有什麼需要測試的。

  • So this will be abstract class failure.

    是以,這將是抽象類的失敗。

  • Let's again extend equitable, so that the class is extending failure later on, should they have any fields like message or some error code or something like that, their equality will be able to be checked based on those fields.

    讓我們再次擴展 equitable,這樣,該類在擴展失敗後,如果有任何字段,如消息或錯誤代碼之類的,就可以根據這些字段檢查它們的平等性。

  • So for that, to use equitable in this abstract base class, we have to have a constructor here, which will accept a list of properties, which will equal const dynamic, which will be an empty list.

    是以,要在這個抽象基類中使用 equitable,我們必須在這裡設置一個構造函數,它將接受一個屬性列表,該列表等於 const dynamic,而 const dynamic 將是一個空列表。

  • And then we will pass this list of properties over to the super constructor.

    然後,我們將把屬性列表傳遞給超級構造函數。

  • This is nothing really to worry about.

    這其實沒什麼好擔心的。

  • This is just what's going on with this Dart analyzer.

    這就是達特分析儀的工作原理。

  • This is just simply the way that things work with equitable.

    這就是公平的工作方式。

  • So we pass in properties over to the super constructor.

    是以,我們將屬性傳遞給超級構造函數。

  • And again, this is only an abstract class.

    再說一遍,這只是一個抽象類。

  • We are going to create concrete classes in the next parts of this course.

    我們將在本課程的後續部分創建具體的類。

  • Then as you hopefully remember from the last part, and as is signified by this diagram here, a repository from which a use case gets its data is both inside the domain layer and inside the data layer as well at the same time.

    希望大家還記得上一部分的內容,正如本圖所示,用例從中獲取數據的存儲庫既位於領域層內部,也同時位於數據層內部。

  • Or to be more precise, the definition of a repository, or as we are going to call it, a repository's contract is present in the domain layer.

    或者更準確地說,資源庫的定義,或者我們將稱之為資源庫的契約,就存在於領域層中。

  • This upper half of this gradient is pink or what is this color or whatever.

    這個漸變的上半部分是粉紅色,或者是什麼顏色之類的。

  • And then the concrete implementation of a repository, this green side of the gradient is in the data layer.

    然後是存儲庫的具體實施,梯度的綠色部分位於數據層。

  • This will allow for total independence of the domain layer.

    這將使域層完全獨立。

  • But there is also another benefit, which we haven't talked about yet.

    但還有一個好處,我們還沒有談到。

  • And that is testability.

    這就是可測試性。

  • That's right.

    這就對了。

  • Testability and separation of concerns go together extremely well.

    可測試性和關注點分離這兩者結合得非常好。

  • And this is the absolute beauty of clean architecture because it really complements test-driven development because without architecture, you cannot even test anything because spaghetti code, believe it or not, cannot be tested.

    這就是簡潔架構的絕對魅力所在,因為它是對測試驅動開發的真正補充,因為沒有架構,你甚至無法測試任何東西,因為意大利麵條代碼(信不信由你)是無法測試的。

  • How will this allow for this?

    這將如何實現?

  • This will be an abstract repository class, will allow us to write tests, test-driven development style, of course, for the use cases, even without having an implementation of the repository.

    這將是一個抽象的存儲庫類,允許我們為用例編寫測試,當然是測試驅動開發風格,甚至不需要存儲庫的實現。

  • Something like this is called mocking.

    這樣的事情叫做嘲弄。

  • And we are, of course, going to get to that later on.

    當然,我們稍後會討論這個問題。

  • For now, let's create the so-called contract of the repository.

    現在,讓我們創建所謂的存儲庫合同。

  • Inside the domain folder, under repositories, let's create a new file, NumberTriviaRepository.dart.

    在域文件夾下的存儲庫中,創建一個新文件 NumberTriviaRepository.dart。

  • And inside of here, we will have an abstract class, NumberTriviaRepository.

    在這裡,我們將有一個抽象類 NumberTriviaRepository。

  • And this class will have the following interface.

    該類將具有以下接口

  • We want to have two methods here, one for getting concrete number trivia, for which we have to pass in a number.

    我們希望在這裡有兩個方法,一個用於獲取具體的數字瑣事,為此我們必須輸入一個數字。

  • And don't worry, I will come back to the return types in just a bit.

    別擔心,我稍後會再談返回類型的問題。

  • So getConcreteNumberTrivia, int number.

    是以,getConcreteNumberTrivia,int number。

  • If I could write number.

    如果我能寫出數字

  • And then, of course, it's going to have getRandomNumberTrivia, which will not have any parameters.

    當然,它還會有 getRandomNumberTrivia(隨機數字瑣事),但不帶任何參數。

  • So let's just rename concrete to random.

    所以,我們就把 "具體 "改名為 "隨機 "吧。

  • And what is going to be its return type?

    它的返回類型是什麼?

  • Well, we are going to use that either type, which I have described previously.

    那麼,我們將使用我之前介紹過的這兩種類型。

  • So let's go bit by bit.

    那我們就一點一點來吧。

  • It will, of course, still return a future, because it will be an asynchronous operation to get the concrete or data sources.

    當然,它仍將返回一個 future,因為這將是一個獲取具體數據或數據源的異步操作。

  • And what's interesting is the type which the future will wrap.

    有趣的是,未來的包裝會是什麼樣的。

  • It will be either, which comes from the darts package.

    要麼是飛鏢包裡的。

  • So let's import that.

    是以,讓我們導入它。

  • And then, either gets two type parameters.

    然後,任一類型都會得到兩個類型參數。

  • One is left, so L, and R for right, of course.

    一個是左,所以是 L,R 當然是右。

  • And the left side is always, for this kind of error handling, the failure.

    對於這種錯誤處理,左側始終是失敗。

  • So let's import failure here.

    所以,讓我們在這裡輸入失敗。

  • And the right side is always the success kind of data.

    右側總是成功的數據。

  • So in this case, it will be the entity number trivia.

    是以,在這種情況下,它將是實體數字瑣事。

  • And we, of course, need to import even that right over here.

    當然,我們也需要把這些東西進口到這裡來。

  • And then this return type will be the same for getRandom number trivia.

    然後,該返回類型將與 getRandom 數字瑣事的返回類型相同。

  • We will cover this in greater detail later on, how to actually work with this either type.

    稍後我們將詳細介紹如何使用這兩種類型。

  • But for now, just know that this function, this method, will return either a failure, which is the left side, or number trivia, which is the right side.

    但現在,我們只需知道,這個函數、這個方法要麼會返回失敗(即左側),要麼會返回數字瑣事(即右側)。

  • And this way, we do not have to deal with catching exceptions anywhere else in the app than in the repository, which will convert the exceptions into failures.

    這樣,我們就不必在應用程序中捕獲異常,而不必在存儲庫中捕獲異常,因為存儲庫會將異常轉換為失敗。

  • And let's use the nifty little extension for VS Code to fix import, so they will be in the relative manner.

    讓我們使用 VS Code 的小擴展來修復導入,這樣它們就會以相對方式導入。

  • Okay, although this part is getting quite long and information-packed already, I do not really wanna leave you hanging.

    好了,雖然這部分已經很長了,信息量也很大,但我真的不想讓你懸著心。

  • So we are finally going to write some tests while implementing the getConcreteNumberTrivia use case.

    是以,我們終於要在實現 getConcreteNumberTrivia 用例時編寫一些測試了。

  • And in the next part, we are gonna add the getRandomNumberTrivia use case.

    下一部分,我們將添加 getRandomNumberTrivia 用例。

  • So definitely stay tuned for that and subscribe to this channel if you do not want to miss it, and also hit the bell button, so that you will get notified about all the new videos.

    是以,如果您不想錯過這些視頻,請務必繼續關注並訂閱本頻道,同時點擊 "鈴鐺 "按鈕,這樣您就會收到所有新視頻的通知。

  • As is the case with test-driven development, we are going to write the test before writing the production code.

    與測試驅動開發一樣,我們將先編寫測試,然後再編寫生產代碼。

  • So this will ensure that we aren't gonna add a bunch of unneeded functionality.

    是以,這將確保我們不會添加大量不需要的功能。

  • This is the YAGNI principle, you ain't gonna need it, which is one of the most powerful things about test-driven development, because it forces you to really think about what you write, because you really do not want to test unnecessary logic.

    這就是 "YAGNI 原則",你不會需要它,這是測試驅動開發最強大的地方之一,因為它迫使你真正思考你所寫的東西,因為你真的不想測試不必要的邏輯。

  • It even doesn't make sense.

    甚至沒有任何意義。

  • You cannot write unnecessary logic with test-driven development, because you really just do not even think about writing something for future-proofing your code.

    使用測試驅動開發時,你不能編寫不必要的邏輯,因為你根本不會考慮編寫一些東西來保護你的代碼。

  • And that is the perfect thing about test-driven development.

    這就是測試驅動開發的完美之處。

  • So let's get right to it.

    那我們就直奔主題吧。

  • Writing the test in Dart apps happens inside the test folder.

    在 Dart 應用程序中編寫測試會在測試文件夾中進行。

  • We have a widget test here, we can delete that right now.

    這裡有一個小部件測試,我們現在就可以刪除它。

  • So move to trash.

    那就搬到垃圾桶去吧。

  • And usually, what we are going to do too, is that test folder structure follows the production code folder structure.

    通常情況下,我們要做的也是讓測試文件夾結構與生產代碼文件夾結構保持一致。

  • So we are going to create all of these folders.

    是以,我們將創建所有這些文件夾。

  • So core will go here.

    是以,核心將放在這裡。

  • Then also new folder features.

    此外,還新增了文件夾功能。

  • Inside the features folder will be another folder called number trivia.

    在 "功能 "文件夾中還有一個名為 "數字瑣事 "的文件夾。

  • And right now, we are going to write only the use case, so the domain folder would be enough to create here.

    現在,我們只打算編寫用例,是以在這裡創建域文件夾就足夠了。

  • But we are going to create all of the other folders as well, so also data.

    但我們也要創建所有其他文件夾,是以也要創建數據。

  • And then presentation, just so that we have everything we need for future lessons.

    然後是演示,這樣我們就能準備好今後上課所需的一切。

  • So we have data, domain presentation.

    是以,我們有數據,有領域展示。

  • Inside the domain, we are going to have the use cases folder, and this is where we are going to write our first test.

    在域中,我們將有一個用例文件夾,這就是我們要編寫第一個測試的地方。

  • Tests in Dart are always named the same way as is the production code file, but we append test to the end.

    Dart 中的測試總是以與生產代碼文件相同的方式命名,但我們會在最後加上 test。

  • So because the production code file will be called get concrete number trivia, the test will be called get concrete number trivia test.dart.

    是以,由於生產代碼文件將被稱為 get concrete number trivia,測試將被稱為 get concrete number trivia test.dart。

  • And again, you can get all of this code from the written tutorial, which is available from the link in the video description.

    同樣,你可以從視頻描述中的鏈接獲取書面教程中的所有代碼。

  • Writing the test requires a bit of a setup.

    編寫測試需要一些設置。

  • We know that the use case should get its data from the number trivia repository, for which we currently have only the contract, the abstract class.

    我們知道,用例應從數字瑣事存儲庫中獲取數據,而我們目前只有該存儲庫的合同和抽象類。

  • Because we have only the abstract class, we are going to mock it so that we can add some functionality to it only for this test.

    因為我們只有抽象類,所以我們要對它進行模擬,這樣我們就可以為它添加一些功能,僅供本次測試使用。

  • And mocking something also allows us to check whether or not some methods have been called on that object.

    通過模擬,我們還可以檢查該對象上的某些方法是否已被調用。

  • And overall, it really allows for nice testing experience.

    總體而言,它確實能帶來不錯的測試體驗。

  • So without further ado, let's create a class, mock number trivia repository, which will extend mock, which comes from the makito.dart package.

    話不多說,讓我們創建一個類--mock number trivia repository,它將擴展 makito.dart 包中的 mock。

  • And then it will, of course, implement first, it needs to implement the number trivia repository.

    當然,它還會首先實現數字瑣事存儲庫。

  • Let's import even that.

    連這個也進口吧。

  • So extending this new class with mock allows us to mock it, of course.

    是以,用 mock 來擴展這個新類,當然就可以模擬它了。

  • And then we implement the interface from the number trivia repository abstract class.

    然後,我們從數字瑣事庫抽象類中實現接口。

  • Let's now also import the flutter test package.

    現在,讓我們同時導入 flutter 測試程序包。

  • So flutter test.dart will go here.

    是以,flutter test.dart 將放在這裡。

  • And now we have to think about how will the use case, which we currently do not even have a file for, and that is perfectly fine with test-driven development, how will the use case operate with the repository?

    現在我們要考慮的是,我們目前甚至還沒有一個用例文件,而測試驅動開發完全可以做到這一點,那麼這個用例將如何與資源庫一起運行呢?

  • Well, of course, it's going to get that repository passed in through the constructor so that later on we can use the getit package to do some nice dependency injection thingies.

    當然,它會通過構造函數傳入資源庫,這樣以後我們就可以使用 getit 套裝軟體做一些漂亮的依賴注入了。

  • This is called loose coupling, and loose coupling is absolutely crucial for test-driven development, because without loose coupling, you cannot test anything, basically.

    這就是所謂的松耦合,松耦合對於測試驅動開發來說絕對是至關重要的,因為沒有松耦合,基本上就無法測試任何東西。

  • And to pass this mocked version of a repository into the now not yet existent use case, we are going to use a method called setup, which is available for every test that you write in Dart.

    為了將這個模擬版本的版本庫傳遞到現在還不存在的用例中,我們將使用一個名為 setup 的方法,你在 Dart 中編寫的每個測試都可以使用這個方法。

  • And the setup method runs before every single test.

    而設置方法會在每次測試前運行。

  • So let's first create void main, because all of the tests run inside this main method.

    是以,讓我們先創建 void main,因為所有測試都在這個 main 方法中運行。

  • And then we have the setup method here, in which we are going to initialize all the objects we need.

    然後,我們在這裡使用設置方法,初始化我們需要的所有對象。

  • But first, we actually have to create the variables in which the objects in question will be stored.

    但首先,我們必須創建變量,將有關對象存儲在其中。

  • So what do we want to have here?

    那麼,我們在這裡想要什麼呢?

  • Well, the now not yet existent get concrete number trivia use case.

    那麼,現在還不存在的獲取具體數字瑣事的用例。

  • So we are going to just call it use case.

    是以,我們將其稱為用例。

  • And then also the mock number trivia repository will be stored here, so that we can then later on get it and do some mocking on this instance.

    然後,模擬數字瑣事庫也將存儲在這裡,這樣我們以後就可以獲取它,並在這個實例上進行一些模擬。

  • So inside setup, we're going to first initialize or instantiate, to be precise, the mock number trivia repository.

    是以,在設置中,我們首先要初始化或實例化(準確地說,是模擬數字瑣事存儲庫)。

  • So let's create a new instance, just like that.

    是以,讓我們創建一個新實例,就像這樣。

  • And then we're going to also instantiate the use case.

    然後,我們還要將用例實例化。

  • So get concrete number trivia use case will be instantiated, and the mock number trivia repository will be passed in.

    是以,具體的數字瑣事用例將被實例化,模擬的數字瑣事存儲庫將被傳入。

  • Of course, it doesn't make any sense to continue with writing the test, because currently, it doesn't even compile.

    當然,繼續編寫測試沒有任何意義,因為目前它甚至無法編譯。

  • So even before writing the actual test, we can do another step in the test driven development, because we have arrived in the red phase.

    是以,即使在編寫實際測試之前,我們也可以完成測試驅動開發的另一個步驟,因為我們已經進入了 "紅色 "階段。

  • TDD works in red, green refactor phases.

    TDD 工作分為紅色、綠色重構階段。

  • Red phase is obvious, because we have red squiggly lines.

    紅色階段很明顯,因為我們有紅色的斜線。

  • So we have to fix something in the red phase.

    是以,我們必須在紅色階段解決一些問題。

  • And now we want to arrive into the green phase.

    現在,我們要進入綠色階段。

  • And then later on, we will refactor.

    之後,我們將進行重構。

  • And we are going to arrive into the green phase, which is without errors, by creating the actual get concrete number trivia class.

    通過創建實際獲取具體數字的瑣事類,我們將進入綠色階段,也就是沒有錯誤的階段。

  • So let's create it under domain use cases here.

    是以,讓我們在這裡的領域用例下創建它。

  • So new file, get concrete number trivia dot dart.

    是以,新文件,獲得具體的數字瑣事點鏢。

  • And this will be just a simple class for now, get concrete number trivia.

    現在,這只是一個簡單的課堂,讓我們來了解具體的數字瑣事。

  • And it will accept a final number trivia repository called repository into its constructor.

    它的構造函數將接受一個名為 repository 的最終數字瑣事存儲庫。

  • So once we import it and create a constructor for missing fields, and fix import so that they are relative, this is the kind of class we have now.

    是以,一旦我們導入它,併為缺失的字段創建一個構造函數,並修復導入,使它們是相對的,這就是我們現在擁有的類。

  • And this is perfectly enough for this test to not complain anymore about non-existent get concrete number trivia use case.

    這就足以讓本測試不再抱怨不存在獲取具體數字的瑣碎用例。

  • And I probably have a typo here.

    我可能打錯了。

  • Of course, get concrete.

    當然,要有混凝土。

  • So we have to rename that concrete.

    是以,我們必須重新命名混凝土。

  • Now we are talking.

    這才對嘛。

  • So now we can test it, anything yet.

    現在我們可以測試它了,什麼都可以。

  • So let's move on right on to that.

    那麼,讓我們繼續往下看。

  • The nature of our number trivia app is really simple.

    數字瑣事應用程序的本質非常簡單。

  • So there will not be a lot to test here in the use case, because really, all it will do is to just get the number trivia data from the repository.

    是以,在這個用例中要測試的東西並不多,因為它要做的只是從存儲庫中獲取數字瑣事數據。

  • And that's basically about it.

    基本上就是這樣。

  • It will just get the entity or the failure, of course, from the repository.

    當然,它只是從存儲庫中獲取實體或故障。

  • To start writing a test, you can either just write test, and then write should get trivia for number from the repository.

    要開始編寫測試,你可以直接編寫測試,然後從資源庫中獲取數字瑣事。

  • Or I have a nice little snippet, which I have created myself, and that is AAA test, which is the arrange, act and assert test.

    或者,我自己創建了一個不錯的小片段,這就是 AAA 測試,即安排、執行和斷言測試。

  • So this test should get trivia for the number from the repository.

    是以,這個測試應該能從資源庫中獲取數字的瑣事。

  • And now, of course, we have already the repository instance present in here, which is the mocked instance of the repository.

    當然,現在我們已經有了版本庫實例,也就是版本庫的模擬實例。

  • But we do not have the trivia and we do not have a number.

    但我們沒有瑣事,也沒有數字。

  • So let's these variables, we want to have final T number, which is the test number, which we are going to try to get from the repository.

    是以,讓我們來看看這些變量,我們希望有最終的 T 編號,也就是測試編號,我們將嘗試從存儲庫中獲取這個編號。

  • So T number is equal to just one.

    是以,T 數字只等於 1。

  • And then final T number trivia is equal to a new instance of number trivia.

    然後,最終 T 數字瑣事等於一個新的數字瑣事實例。

  • This is what is going to be returned from the repository.

    這就是從存儲庫返回的內容。

  • We want to have a regular instance of it here.

    我們希望在這裡有一個固定的實例。

  • So it accepts a number, which will be the T number, or we can actually just pass one in here.

    是以,它接受一個數字,也就是 T 編號,或者我們可以在這裡直接傳入一個數字。

  • It doesn't really matter.

    這並不重要。

  • And then the text will be again, it doesn't matter what the text is.

    然後又是文字,文字是什麼並不重要。

  • So now in the arrange phase of this test, what we want to do is to provide some functionality to the mocked instance of the repository.

    是以,在測試的安排階段,我們要做的是為模擬的版本庫實例提供一些功能。

  • So when, and this is really nice, the syntax that is here, because you can really read it as an English sentence and it will all make sense.

    所以,當你把它當作一個英語句子來讀時,它的文法就會變得非常好,因為你真的可以把它當作一個英語句子來讀,一切都會變得合情合理。

  • So when mock number trivia repository, get concrete number trivia is called on it with any argument.

    是以,在模擬數字瑣事存儲庫中,可以使用任何參數調用獲取具體數字瑣事。

  • So it doesn't matter whether or not the number is one or two or three, it can be really anything.

    是以,數字是一個、兩個還是三個並不重要,它可以是任何東西。

  • So any is a matcher.

    是以,任何都是匹配器。

  • Then answer, because it's asynchronous, so you cannot call then return, but you have to call then answer.

    然後應答,因為它是異步的,所以不能先調用再返回,而必須先調用再應答。

  • And the answer will be asynchronous.

    答案將是異步。

  • And what do we want to answer?

    我們想回答什麼?

  • Well, the return type of get concrete number trivia is an either and the right side of the either type is number trivia and the left side is failure.

    那麼,get concrete number trivia 的返回類型是任一類型,任一類型的右邊是數字瑣事,左邊是失敗。

  • Well, in the test, it doesn't really matter what we return.

    在測試中,我們返回什麼並不重要。

  • We can return really either failure or the number of trivia.

    我們可以真正返回失敗或瑣事的數量。

  • In this case, let's return the number trivia.

    在這種情況下,讓我們返回數字瑣事。

  • So we want to return the right side of the either type.

    是以,我們希望返回任一類型的右側。

  • This is how we really use it.

    我們就是這樣使用它的。

  • And then later on, you can also check whether or not the side of the returned object is left or right.

    之後,還可以檢查返回對象的邊是左還是右。

  • If it's left, you know that it's a failure.

    如果離開了,你就知道失敗了。

  • If it's right, you know that it's a successful object returned.

    如果是正確的,就說明對象返回成功。

  • So right, let's import darts here.

    好吧,我們來進口飛鏢。

  • We want to return right the number trivia.

    我們要回到數字瑣事上來。

  • So to go through this again, when the mock number trivia repository, get concrete number trivia method is called with any argument, any number, then always answer with the success response, so to say.

    是以,再重複一遍,當使用任何參數、任何數字調用模擬數字瑣事存儲庫、獲取具體數字瑣事方法時,總是以成功響應來回答。

  • So the right side of the either with number trivia contained inside of it.

    是以,右側的任一數字瑣事都包含在其中。

  • All of this is available from the written tutorial.

    所有這些都可以從書面教程中獲得。

  • So definitely, if you want to learn at your own pace, check that out from the link in the video description.

    是以,如果你想按照自己的節奏學習,可以通過視頻描述中的鏈接查看。

  • Now we move on to the act phase of the test, which is usually really simple.

    現在,我們進入測試的行動階段,這通常非常簡單。

  • So we just want to store the result of calling the use case.

    是以,我們只想存儲調用用例的結果。

  • And we want to call the use case, which is asynchronous.

    我們要調用的用例是異步的。

  • So we want to call await use case dot execute.

    是以,我們要調用 await 用例 dot execute。

  • And of course, there is no such method yet, because we have not implemented it, because we are doing test-driven development.

    當然,現在還沒有這種方法,因為我們還沒有實現它,因為我們正在進行測試驅動開發。

  • So we want to call execute on that use case and pass in the number, the number here.

    是以,我們要調用該用例的執行程序,並在這裡輸入數字。

  • Now comes the assert part of the test.

    現在是測試的斷言部分。

  • So what do we want to check for?

    那麼,我們要檢查什麼呢?

  • Well, we expect that the result of the execute method on the use case will really be the same thing as was just returned from the mock number trivia repository, because again, the stuff that use case does is really simple.

    我們希望用例執行方法的結果與剛剛從模擬數字瑣事庫中返回的結果相同,因為用例所做的事情非常簡單。

  • It just gets the data from the repository.

    它只是從存儲庫中獲取數據。

  • And in the case of number trivia app, it doesn't do anything more than that.

    就數字瑣事應用程序而言,它所做的不過如此。

  • So therefore, we expect the result to be right.

    是以,我們希望結果是正確的。

  • And inside of it, we expect that there will be the number trivia present.

    而在其中,我們預計會有數字瑣事出現。

  • And then we also want to verify using the Mockito package.

    然後,我們還要使用 Mockito 套裝軟體進行驗證。

  • We want to verify that mock number trivia repositories get concrete number trivia was called with the number.

    我們要驗證模擬數字瑣事庫是否獲得了具體的數字瑣事被調用的數字。

  • This is pretty important, because imagine that we pass in the number into the use case.

    這一點非常重要,因為試想一下,我們把數字輸入到用例中。

  • So just to make it more visually concrete, let's pass in number 20.

    為了更直觀具體,讓我們通過第 20 號。

  • But somehow in the use case, some smart guy decided that he will always pass the number 42 over to get concrete number trivia in the repository.

    但在使用案例中,某個聰明人決定總是將數字 42 傳遞過去,以便在存儲庫中獲取具體的數字瑣事。

  • If we did not have this verification here, we would find out about it, of course, because the response would be not correct.

    如果我們這裡沒有這種驗證,我們當然會發現它,因為答覆是不正確的。

  • But this verification adds another layer of protection to our code against these kinds of errors, which do not just pass the correct arguments to the methods of the dependencies of the use case or any other class there is.

    但這種驗證為我們的代碼增加了另一層保護,以防止出現此類錯誤,因為這些錯誤不僅僅是將正確的參數傳遞給用例或任何其他類的依賴方法。

  • So let's again, revert back to the number.

    所以,讓我們再次回到數字上來。

  • And finally, we are just going to verify no more interactions are happening in the on the mock number trivia repository, because once we call execute, the use case should not do anything more with the repository.

    最後,我們將驗證模擬數字瑣事存儲庫中不再發生任何交互,因為一旦我們調用執行,用例就不應該再對存儲庫做任何事情。

  • There is again, no point in even running this test, because it's not going to run since it doesn't even compile.

    同樣,運行這個測試也沒有任何意義,因為它根本不會運行,因為它根本沒有編譯。

  • We have a compilation error here that execute is not present on the use case.

    這裡出現了一個編譯錯誤,用例中沒有執行。

  • This test really acts like a documentation for the functionality, which we are about to implement.

    這個測試實際上就像是我們即將實現的功能的文檔。

  • So let's jump right into that.

    那我們就直接進入主題吧。

  • We're going to go to get concrete number trivia and start writing the implementation of execute method.

    我們將獲取具體的數字瑣事,並開始編寫執行方法的實現。

  • So it will return future either, which contains failure or number trivia.

    是以,它將返回包含失敗或數字瑣事的未來。

  • So we need to import all of these types over here.

    是以,我們需要在這裡導入所有這些類型。

  • Once we give this method a name, execute.

    給這個方法命名後,就可以執行了。

  • So let's import darts, let's import failures, and let's import number trivia.

    是以,讓我們引進飛鏢,引進失敗,引進數字瑣事。

  • Now based on the test, we know that execute method should take in a number, which is a named parameter.

    根據測試結果,我們知道 execute 方法應該接收一個數字,即命名參數。

  • So therefore, we know what we need to do.

    是以,我們知道我們需要做什麼。

  • It will have a named required, of course.

    當然,它將有一個指定的要求。

  • So we need to import meta also, meta.dart, required number, which is an integer.

    是以,我們還需要導入 meta、meta.dart、所需的數字,這是一個整數。

  • And it's going to just call the repository that get concrete number trivia with the number.

    它將調用獲取具體數字瑣事的存儲庫,並提供數字。

  • And of course, we need to return a weight, whatever was gotten from the repository.

    當然,我們還需要返回一個權重,即從存儲庫中獲取的權重。

  • So this function needs to be itself asynchronous.

    是以,這個函數本身需要是異步的。

  • All right.

    好的

  • So this is the implementation for the execute method.

    這就是執行方法的實現。

  • And now all we need to do is to run the test.

    現在我們要做的就是運行測試。

  • I have a handy shortcut, keyboard shortcut for running all of the dart tests.

    我有一個方便的快捷鍵,即運行所有飛鏢測試的鍵盤快捷鍵。

  • So I will show you how to do that.

    所以,我會告訴你如何做到這一點。

  • If you are using VS Code, of course, in IntelliJ, this would be completely different.

    當然,如果您使用的是 IntelliJ 中的 VS Code,情況就完全不同了。

  • But in VS Code, go to file, preferences, keyboard shortcuts, and now search for dart run all tests.

    但在 VS 代碼中,進入文件、首選項、鍵盤快捷鍵,然後搜索 dart run all tests。

  • And for this command, just set some key binding.

    對於這條命令,只需設置一些按鍵綁定。

  • I have shift, alt, and left square bracket.

    我有 shift、alt 和左方括號。

  • So once you have this shortcut, you can run all of the tests with shift, alt, and left square bracket.

    是以,一旦有了這個快捷鍵,你就可以用 shift、alt 和左側方括號運行所有測試。

  • We have only one test, of course.

    當然,我們只有一個測試。

  • And it's going to pass.

    而且會通過。

  • So we should get trivia for the number from the repository.

    是以,我們應該從存儲庫中獲取有關數字的瑣事。

  • This is only a simple test.

    這只是一個簡單的測試。

  • That's because the functionality of the get concrete number trivia use case is also simple, but it in our code.

    這是因為獲取具體數字瑣事用例的功能也很簡單,但它在我們的代碼中。

  • And the other way to run a test is to go over to the debugging tab here in VS Code.

    運行測試的另一種方法是轉到 VS 代碼中的調試選項卡。

  • Now tap on this configure or fix launch.json here icon.

    現在點擊這裡的配置或修復 launch.json 圖標。

  • Ignore .NET if something like that pops up.

    如果出現類似情況,請忽略 .NET。

  • And we want to add configuration.

    我們要添加配置。

  • So dart run all tests.

    是以,飛鏢運行了所有測試。

  • All right.

    好的

  • And with this configuration present in the launch.json file, you can now select dart run all tests from the dropdown in the debug tab of VS Code.

    有了 launch.json 文件中的配置,現在就可以在 VS Code 調試選項卡的下拉菜單中選擇 dart 運行所有測試了。

  • So once you do that, you can run all tests even by clicking this green start debugging button.

    這樣,只要點擊綠色的 "開始調試 "按鈕,就可以運行所有測試了。

  • And again, it will pass because the same test was run again.

    同樣,它也會通過,因為又進行了同樣的測試。

  • In the next part, we are going to add another use case for getting the random number trivia.

    下一部分,我們將為獲取隨機數小知識添加另一個用例。

  • And also we are going to refactor this code and also the new use cases code so that it will be more robust.

    此外,我們還將重構這些代碼和新的用例代碼,使其更加健壯。

  • So definitely stay tuned for that.

    敬請期待。

  • And if you don't want to miss the next part and also more tutorials like this, definitely subscribe to this channel and also join the notification squad by hitting the bell button to make sure you grow your Flutter coding skills because here on ResoCoder, I am determined to provide you with the best app development tutorials and resources out there.

    如果你不想錯過下一部分和更多類似的教程,請務必訂閱本頻道,並點擊鈴聲按鈕加入通知小隊,以確保你的 Flutter 編碼技能得到提高,因為在 ResoCoder,我決心為你提供最好的應用程序開發教程和資源。

  • If this video helped you with understanding at least just a bit test-driven development process, and do not worry, we are going to write many, many more tests throughout this series.

    如果這段視頻至少幫助你瞭解了一點測試驅動開發流程,別擔心,在整個系列中,我們還將編寫更多測試。

  • Give this video a like and also share it with our developers who will surely benefit from it as well.

    請點贊這段視頻,並與我們的開發人員分享,他們一定也會從中受益。

  • Leave a comment if you have anything to say, any questions regarding this test-driven development, I will try to answer them to my best ability.

    如果您有任何意見或關於測試驅動開發的任何問題,請留言給我,我會盡力回答。

  • Follow me on Instagram, I go under the name ResoCoder everywhere so that you get some behind-the-scenes news from what I am currently doing, and see you in the next video.

    在 Instagram 上關注我,我的名字是 ResoCoder,這樣你就能獲得一些我正在做的事情的幕後消息,我們下一個視頻再見。

In the first part, you learned the core concepts of clean architecture as it pertains to Flutter.

在第一部分中,您學習了與 Flutter 相關的簡潔架構的核心概念。

字幕與單字
由 AI 自動生成

單字即點即查 點擊單字可以查詢單字解釋