Chúng ta có nên xác định loại cho tất cả mọi thứ?


141

Gần đây tôi gặp vấn đề với khả năng đọc mã của tôi.
Tôi đã có một chức năng đã thực hiện một thao tác và trả về một chuỗi đại diện cho ID của hoạt động này để tham khảo trong tương lai (giống như OpenFile trong Windows trả về một điều khiển). Người dùng sẽ sử dụng ID này sau để bắt đầu hoạt động và theo dõi kết thúc của nó.

ID phải là một chuỗi ngẫu nhiên vì mối quan tâm về khả năng tương tác. Điều này tạo ra một phương thức có chữ ký rất không rõ ràng như vậy:

public string CreateNewThing()

Điều này làm cho ý định của loại trả lại không rõ ràng. Tôi nghĩ để bọc chuỗi này trong một loại khác làm cho ý nghĩa của nó rõ ràng hơn như vậy:

public OperationIdentifier CreateNewThing()

Loại sẽ chỉ chứa chuỗi và được sử dụng bất cứ khi nào chuỗi này được sử dụng.

Rõ ràng rằng lợi thế của cách vận hành này là an toàn hơn về loại hình và mục đích rõ ràng hơn, nhưng nó cũng tạo ra nhiều mã và mã hơn rất nhiều thành ngữ. Một mặt tôi thích sự an toàn được thêm vào, nhưng nó cũng tạo ra rất nhiều sự lộn xộn.

Bạn có nghĩ rằng đó là một thực hành tốt để bọc các loại đơn giản trong một lớp học vì lý do an toàn?


4
Bạn có thể thích đọc về các loại Tiny. Tôi đã chơi với nó một chút và sẽ không khuyên bạn nhưng đó là một cách tiếp cận thú vị để suy nghĩ. darrenhobbs.com/2007/04/11/tiny-types
Jeroen Vannevel

40
Khi bạn sử dụng một cái gì đó như Haskell, bạn sẽ thấy việc sử dụng các loại giúp ích như thế nào. Các loại giúp đảm bảo một chương trình chính xác.
Symate Nate

3
Ngay cả một ngôn ngữ động như Erlang cũng được hưởng lợi từ một hệ thống loại, đó là lý do tại sao nó có một hệ thống typecec và công cụ đánh máy quen thuộc với Haskeller mặc dù là ngôn ngữ động. Các ngôn ngữ như C / C ++ trong đó loại thực của bất cứ thứ gì là một số nguyên mà chúng tôi đồng ý với trình biên dịch để xử lý theo một cách nhất định hoặc Java nơi bạn gần như sắp xếp các loại nếu bạn quyết định giả sử các lớp là loại có vấn đề quá tải ngữ nghĩa trong ngôn ngữ (đây là "lớp" hay "loại"?) hoặc kiểu nhập nhằng (đây có phải là "URL", "Chuỗi" hoặc "UPC" không?). Cuối cùng, sử dụng bất kỳ công cụ nào bạn có thể để định hướng.
zxq9

7
Tôi không chắc ý tưởng xuất phát từ đâu mà C ++ có chi phí hoạt động. Với C ++ 11, không ai trong số các nhà sản xuất trình biên dịch gặp vấn đề trong việc cung cấp cho mỗi lambda loại duy nhất của riêng mình. Nhưng TBH bạn thực sự không thể mong đợi sẽ tìm thấy nhiều thông tin chi tiết trong một nhận xét về "hệ thống loại C / C ++" như thể cả hai chia sẻ một hệ thống loại.
MSalters

2
@JDB - không, không. Thậm chí không giống nhau.
Thưởng thức Ždralo

Câu trả lời:


152

Nguyên thủy, chẳng hạn như stringhoặc int, không có ý nghĩa trong một lĩnh vực kinh doanh. Hậu quả trực tiếp của việc này là bạn có thể sử dụng nhầm URL khi ID sản phẩm được mong đợi hoặc sử dụng số lượng khi mong đợi giá .

Đây cũng là lý do tại sao thử thách Object Calisthenics có tính năng bao bọc nguyên thủy như một trong những quy tắc của nó:

Quy tắc 3: Bọc tất cả các nguyên thủy và Chuỗi

Trong ngôn ngữ Java, int là một nguyên thủy, không phải là một đối tượng thực sự, vì vậy nó tuân theo các quy tắc khác với các đối tượng. Nó được sử dụng với một cú pháp không hướng đối tượng. Quan trọng hơn, một int riêng của nó chỉ là một vô hướng, vì vậy nó không có ý nghĩa. Khi một phương thức lấy một int làm tham số, tên phương thức cần thực hiện tất cả các công việc thể hiện ý định. Nếu cùng một phương thức lấy một giờ làm tham số, thì việc xem những gì đang diễn ra sẽ dễ dàng hơn nhiều.

Tài liệu tương tự giải thích rằng có một lợi ích bổ sung:

Các đối tượng nhỏ như Giờ hoặc Tiền cũng cho chúng ta một vị trí rõ ràng để đặt hành vi mà nếu không sẽ bị vứt bừa bãi xung quanh các lớp khác .

Thật vậy, khi các nguyên thủy được sử dụng, thường rất khó để theo dõi vị trí chính xác của mã liên quan đến các loại đó, thường dẫn đến sao chép mã nghiêm trọng . Nếu có Price: Moneylớp, việc tìm phạm vi kiểm tra bên trong là điều tự nhiên. Nếu, thay vào đó, một int(tệ hơn, a double) được sử dụng để lưu trữ giá sản phẩm, ai nên xác nhận phạm vi? Sản phẩm? Việc giảm giá? Các xe đẩy?

Cuối cùng, một lợi ích thứ ba không được đề cập trong tài liệu là khả năng thay đổi tương đối dễ dàng loại cơ bản. Nếu hôm nay tôi ProductIdshortloại cơ bản và sau này tôi cần sử dụng intthay vào đó, rất có thể mã để thay đổi sẽ không bao trùm toàn bộ cơ sở mã.

Hạn chế và một lập luận tương tự áp dụng cho mọi quy tắc của bài tập Object Calisthenics là nếu nhanh chóng trở nên quá sức để tạo ra một lớp cho mọi thứ . Nếu Productchứa ProductPricenhững thứ thừa kế từ PositivePriceđó thừa kế từ Priceđó thừa hưởng từ đó Money, thì đây không phải là kiến ​​trúc sạch, mà là một mớ hỗn độn hoàn toàn để tìm một thứ duy nhất, một người bảo trì nên mở vài chục tệp mỗi lần.

Một điểm khác cần xem xét là chi phí (về dòng mã) của việc tạo các lớp bổ sung. Nếu các trình bao bọc là bất biến (thường là như vậy), điều đó có nghĩa là, nếu chúng tôi lấy C #, bạn phải có, trong ít nhất là trong trình bao bọc:

  • Người nhận tài sản,
  • Lĩnh vực hỗ trợ của nó,
  • Một hàm tạo gán giá trị cho trường sao lưu,
  • Một phong tục ToString(),
  • Nhận xét tài liệu XML (tạo ra rất nhiều dòng),
  • A EqualsGetHashCodeghi đè (cũng rất nhiều LỘC).

và cuối cùng, khi có liên quan:

  • Một DebuggerDisplay thuộc tính,
  • Ghi đè ==!=toán tử,
  • Cuối cùng, quá tải toán tử chuyển đổi ngầm định để chuyển đổi liền mạch đến và từ loại được đóng gói,
  • Hợp đồng mã (bao gồm cả bất biến, là một phương thức khá dài, với ba thuộc tính của nó),
  • Một số bộ chuyển đổi sẽ được sử dụng trong quá trình tuần tự hóa XML, tuần tự hóa JSON hoặc lưu trữ / tải một giá trị đến / từ cơ sở dữ liệu.

Một trăm LỘC cho một trình bao bọc đơn giản làm cho nó khá cấm, đó cũng là lý do tại sao bạn có thể hoàn toàn chắc chắn về lợi nhuận lâu dài của trình bao bọc đó. Khái niệm phạm vi được giải thích bởi Thomas Junk đặc biệt có liên quan ở đây. Viết một trăm LỘC để thể hiện một ProductIdsử dụng trên toàn bộ cơ sở mã của bạn trông khá hữu ích. Viết một lớp có kích thước này cho một đoạn mã làm cho ba dòng trong một phương thức trở nên nghi ngờ hơn nhiều.

Phần kết luận:

  • Không bao gồm các nguyên hàm trong các lớp có ý nghĩa trong lĩnh vực kinh doanh của ứng dụng khi (1) nó giúp giảm lỗi, (2) giảm nguy cơ sao chép mã hoặc (3) giúp thay đổi loại cơ bản sau này.

  • Đừng bao bọc tự động mọi nguyên thủy bạn tìm thấy trong mã của mình: có nhiều trường hợp sử dụng stringhoặc inthoàn toàn tốt.

Trong thực tế, trong public string CreateNewThing(), trả về một thể hiện của ThingIdlớp thay vì stringcó thể giúp đỡ, nhưng bạn cũng có thể:

  • Trả về một thể hiện của Id<string>lớp, đó là một đối tượng của kiểu chung cho biết kiểu bên dưới là một chuỗi. Bạn có lợi ích về khả năng đọc, không có nhược điểm là phải duy trì nhiều loại.

  • Trả về một thể hiện của Thinglớp. Nếu người dùng chỉ cần ID, điều này có thể dễ dàng thực hiện với:

    var thing = this.CreateNewThing();
    var id = thing.Id;
    

33
Tôi nghĩ rằng sức mạnh để tự do thay đổi loại cơ bản là quan trọng ở đây. Hôm nay là một chuỗi, nhưng có lẽ là một GUID hoặc ngày mai dài. Tạo trình bao bọc loại sẽ che giấu thông tin đó dẫn đến ít thay đổi hơn. ++ Câu trả lời tốt ở đây.
RubberDuck

4
Trong trường hợp có tiền, hãy đảm bảo tiền tệ được bao gồm trong đối tượng Tiền hoặc toàn bộ chương trình của bạn sử dụng cùng một loại (sau đó sẽ được ghi lại).
Paŭlo Ebermann

5
@Blrfl: trong khi có những trường hợp hợp lệ khi cần thừa kế sâu, tôi thường thấy sự thừa kế đó trong mã được viết quá mức được viết mà không chú ý đến khả năng đọc, do thái độ trả tiền bắt buộc của LỘC. Do đó, nhân vật tiêu cực của phần này trong câu trả lời của tôi.
Arseni Mourzenko

3
@ArTs: Đó là lập luận "hãy lên án công cụ vì một số người sử dụng sai cách".
Blrfl

6
@MainMa Một ví dụ về ý nghĩa có thể tránh được một lỗi tốn kém: en.wikipedia.org/wiki/Mars_Climate_Orbiter#Cause_of_failure
cbojar

60

Tôi sẽ sử dụng phạm vi như một quy tắc chung: Phạm vi tạo và tiêu thụ càng hẹp thì valuesbạn càng ít có khả năng tạo một đối tượng đại diện cho giá trị này.

Giả sử bạn có mã giả sau đây

id = generateProcessId();
doFancyOtherstuff();
job.do(id);

sau đó phạm vi rất hạn chế và tôi sẽ không có ý nghĩa gì trong việc biến nó thành một loại. Nhưng giả sử, bạn tạo giá trị này trong một lớp và chuyển nó sang một lớp khác (hoặc thậm chí là một đối tượng khác), thì nó sẽ có ý nghĩa hoàn hảo để tạo ra một loại cho điều đó.


14
Một điểm rất tốt. Các loại rõ ràng là một công cụ quan trọng để truyền đạt ý định , đặc biệt quan trọng xuyên qua các ranh giới đối tượng và dịch vụ.
Avner Shahar-Kashtan

+1 mặc dù nếu bạn chưa tái cấu trúc tất cả mã của doFancyOtherstuff()chương trình con, bạn có thể nghĩ rằng job.do(id)tham chiếu không đủ cục bộ để giữ như một chuỗi đơn giản.
Đánh dấu Hurd

Điều này là hoàn toàn đúng! Đó là trường hợp khi bạn có một phương thức riêng tư trong đó bạn biết rằng thế giới bên ngoài sẽ không bao giờ sử dụng phương thức đó. Sau đó, bạn có thể suy nghĩ thêm một chút khi lớp được bảo vệ và sau đó khi lớp được công khai nhưng không phải là một phần của api công khai. Phạm vi phạm vi phạm vi và rất nhiều nghệ thuật để đặt dòng ở vị trí tốt giữa nhiều loại nguyên thủy và nhiều loại tùy chỉnh.
Samuel

34

Các hệ thống loại tĩnh là tất cả về việc ngăn chặn việc sử dụng dữ liệu không chính xác.

Có những ví dụ rõ ràng về các loại làm điều này:

  • bạn không thể có được một tháng của UUID
  • bạn không thể nhân hai chuỗi.

Có nhiều ví dụ tinh tế hơn

  • bạn không thể trả tiền cho một cái gì đó bằng cách sử dụng chiều dài của bàn
  • bạn không thể thực hiện yêu cầu HTTP bằng tên của ai đó làm URL.

Chúng tôi có thể bị cám dỗ để sử dụng doublecho cả giá cả và độ dài hoặc sử dụng stringcho cả tên và URL. Nhưng làm điều đó sẽ thay đổi hệ thống loại tuyệt vời của chúng tôi và cho phép những kẻ lạm dụng này vượt qua kiểm tra tĩnh của ngôn ngữ.

Bắt pound-giây nhầm lẫn với Newton-giây có thể có kết quả kém khi chạy .


Điều này đặc biệt là một vấn đề với chuỗi. Chúng thường trở thành "kiểu dữ liệu phổ quát".

Chúng ta thường sử dụng các giao diện văn bản chủ yếu với máy tính và chúng ta thường mở rộng các giao diện người dùng (UI) này sang các giao diện lập trình (API). Chúng tôi nghĩ 34,25 là các nhân vật 34,25 . Chúng tôi nghĩ về một ngày như các nhân vật 05 / 03-2015 . Chúng tôi nghĩ về một UUID như các ký tự 75e945ee-f1e9-11e4-b9b2-1697f925ec7b .

Nhưng mô hình tinh thần này gây hại cho sự trừu tượng API.

Các từ hoặc ngôn ngữ, khi chúng được viết hoặc nói, dường như không đóng vai trò gì trong cơ chế suy nghĩ của tôi.

Albert Einstein

Tương tự, các biểu diễn văn bản không nên đóng bất kỳ vai trò nào trong việc thiết kế các loại và API. Coi chừng string! (và các loại "nguyên thủy" quá chung chung khác)


Các loại giao tiếp "những gì hoạt động có ý nghĩa."

Ví dụ: tôi đã từng làm việc trên máy khách với API HTTP REST. REST, được thực hiện đúng cách, sử dụng các thực thể hypermedia, có các siêu liên kết trỏ đến các thực thể liên quan. Trong ứng dụng khách này, không chỉ các thực thể được nhập (ví dụ: Người dùng, Tài khoản, Đăng ký), các liên kết đến các thực thể đó cũng được nhập (UserLink, AccountLink, Thuê bao Liên kết). Các liên kết ít hơn các trình bao bọc xung quanh Uri, nhưng các loại riêng biệt khiến chúng tôi không thể thử sử dụng AccountLink để tìm nạp Người dùng. Nếu mọi thứ đều đơn giản Uri- hoặc thậm chí tệ hơn, string- những lỗi này sẽ chỉ được tìm thấy trong thời gian chạy.

Tương tự như vậy, trong tình huống của bạn, bạn có dữ liệu chỉ được sử dụng cho một mục đích: để xác định một Operation. Nó không nên được sử dụng cho bất cứ điều gì khác và chúng ta không nên cố gắng xác định Operations với các chuỗi ngẫu nhiên mà chúng ta đã tạo nên. Tạo một lớp riêng biệt thêm khả năng đọc và an toàn cho mã của bạn.


Tất nhiên, tất cả những điều tốt đẹp có thể được sử dụng vượt quá. Xem xét

  1. bao nhiêu sự rõ ràng nó thêm vào mã của bạn

  2. tần suất sử dụng

Nếu một "loại" dữ liệu (theo nghĩa trừu tượng) được sử dụng thường xuyên, cho các mục đích riêng biệt và giữa các giao diện mã, thì đó là một ứng cử viên rất tốt để trở thành một lớp riêng biệt, với chi phí dài.


21
"Các chuỗi thường trở thành kiểu dữ liệu" phổ quát "- đó là nguồn gốc của biệt danh" ngôn ngữ được gõ theo kiểu lưỡi ".
MSalters

@MSalters, ha ha, rất đẹp.
Paul Draper

4
Và tất nhiên, có 05-03-2015 nghĩa làkhi được hiểu là một ngày ?
CVn

10

Bạn có nghĩ rằng đó là một thực hành tốt để bọc các loại đơn giản trong một lớp học vì lý do an toàn?

Đôi khi.

Đây là một trong những trường hợp bạn cần cân nhắc các vấn đề có thể phát sinh từ việc sử dụng stringthay vì cụ thể hơn OperationIdentifier. Mức độ nghiêm trọng của họ là gì? Khả năng của họ là gì?

Sau đó, bạn cần xem xét chi phí sử dụng loại khác. Nó đau như thế nào khi sử dụng? Bao nhiêu công việc để làm?

Trong một số trường hợp, bạn sẽ tiết kiệm thời gian và công sức cho mình bằng cách có một loại bê tông đẹp để chống lại. Trong những người khác, nó sẽ không có giá trị rắc rối.

Nói chung, tôi nghĩ rằng loại điều này nên được thực hiện nhiều hơn ngày nay. Nếu bạn có một thực thể có nghĩa là một cái gì đó trong miền của bạn, thật tốt khi có loại đó là loại riêng của nó, vì thực thể đó có nhiều khả năng thay đổi / phát triển cùng với doanh nghiệp.


10

Tôi thường đồng ý rằng nhiều lần bạn nên tạo một loại cho các nguyên hàm và chuỗi, nhưng vì các câu trả lời ở trên khuyên bạn nên tạo một loại trong hầu hết các trường hợp, tôi sẽ liệt kê một vài lý do tại sao / khi nào không:

  • Hiệu suất. Tôi phải tham khảo các ngôn ngữ thực tế ở đây. Trong C #, nếu ID khách hàng ngắn và bạn bọc nó trong một lớp - bạn vừa tạo ra rất nhiều chi phí: bộ nhớ, vì giờ đây nó có 8 byte trên hệ thống 64 bit và tốc độ kể từ bây giờ được phân bổ trên heap.
  • Khi bạn thực hiện các giả định về loại. Nếu ID khách hàng ngắn và bạn có một số logic đóng gói nó theo một cách nào đó - bạn thường đã đưa ra các giả định về loại. Ở tất cả những nơi đó bây giờ bạn phải phá vỡ sự trừu tượng. Nếu đó là một vị trí, đó không phải là vấn đề lớn, nếu nó ở khắp mọi nơi, bạn có thể thấy rằng một nửa thời gian bạn đang sử dụng nguyên thủy.
  • Bởi vì không phải ngôn ngữ nào cũng có typedef. Và đối với các ngôn ngữ không được viết và mã đã được viết, thực hiện thay đổi như vậy có thể là một nhiệm vụ lớn thậm chí có thể gây ra lỗi (nhưng mọi người đều có bộ kiểm tra có độ bao phủ tốt, phải không?).
  • Trong một số trường hợp, nó làm giảm khả năng đọc. Làm thế nào để tôi in này? Làm thế nào để tôi xác nhận điều này? Tôi có nên kiểm tra null không? Có phải tất cả các câu hỏi yêu cầu bạn đi sâu vào định nghĩa của loại.

3
Chắc chắn nếu loại trình bao bọc là một cấu trúc chứ không phải là một lớp, thì một short(ví dụ) có cùng chi phí được bọc hoặc không được bao bọc. (?)
Toán học

1
Nó sẽ không phải là thiết kế tốt cho một loại để đóng gói xác nhận của chính nó? Hoặc để tạo một loại người trợ giúp để xác nhận nó?
Nick Udell

1
Đóng gói hoặc munging không cần thiết là một mô hình chống. Thông tin nên lưu chuyển trong suốt thông qua mã ứng dụng, trừ khi việc chuyển đổi rõ ràng và thực sự cần thiết . Các loại là tốt vì chúng thường thay thế một lượng nhỏ mã tốt, được đặt ở một nơi duy nhất, cho một lượng lớn triển khai kém nằm rải rác trên toàn hệ thống.
Thomas W

7

Không, bạn không nên xác định loại (lớp) cho "mọi thứ".

Nhưng, như các câu trả lời khác, nó thường hữu ích để làm như vậy. Bạn nên phát triển - một cách có ý thức, nếu có thể - một cảm giác hoặc cảm giác quá ma sát do không có một loại hoặc lớp phù hợp, khi bạn viết, kiểm tra và duy trì mã của bạn. Đối với tôi, sự khởi đầu của quá nhiều ma sát là khi tôi muốn hợp nhất nhiều giá trị nguyên thủy thành một giá trị duy nhất hoặc khi tôi cần xác thực các giá trị (nghĩa là xác định giá trị nào trong tất cả các giá trị có thể có của loại nguyên thủy tương ứng với các giá trị hợp lệ của 'loại ngụ ý').

Tôi đã tìm thấy những cân nhắc như những gì bạn đặt ra trong câu hỏi của bạn phải chịu trách nhiệm cho quá nhiều thiết kế trong mã của tôi. Tôi đã hình thành thói quen cố tình tránh viết bất kỳ mã nào nhiều hơn mức cần thiết. Hy vọng rằng bạn đang viết các bài kiểm tra (tự động) tốt cho mã của mình - nếu là bạn, thì bạn có thể dễ dàng cấu trúc lại mã của mình và thêm các loại hoặc các lớp nếu làm như vậy mang lại lợi ích ròng cho việc phát triển và bảo trì mã của bạn .

Câu trả lời của Telastyncâu trả lời của Thomas Junk đều đưa ra quan điểm rất hay về bối cảnh và cách sử dụng mã liên quan. Nếu bạn đang sử dụng một giá trị bên trong một khối mã (ví dụ: phương thức, vòng lặp, usingkhối) thì chỉ nên sử dụng một kiểu nguyên thủy. Thậm chí sử dụng loại nguyên thủy là tốt nếu bạn đang sử dụng một bộ giá trị lặp đi lặp lại và ở nhiều nơi khác. Nhưng bạn càng thường xuyên sử dụng một tập hợp các giá trị và tập hợp các giá trị đó càng ít tương ứng với các giá trị được biểu thị bởi kiểu nguyên thủy, bạn càng nên xem xét việc đóng gói các giá trị trong một lớp hoặc loại.


5

Nhìn bề ngoài, có vẻ như tất cả những gì bạn cần làm là xác định một thao tác.

Ngoài ra, bạn nói những gì một hoạt động được cho là phải làm:

để bắt đầu hoạt động [và] để theo dõi kết thúc của nó

Bạn nói nó theo cách như thể đây là "cách sử dụng nhận dạng", nhưng tôi sẽ nói đây là những đặc tính mô tả một hoạt động. Nghe có vẻ giống như một định nghĩa kiểu đối với tôi, thậm chí còn có một mẫu gọi là "mẫu lệnh" rất phù hợp. .

Nó nói rằng

mẫu lệnh [...] được sử dụng để đóng gói tất cả thông tin cần thiết để thực hiện một hành động hoặc kích hoạt một sự kiện sau đó .

Tôi nghĩ rằng điều này rất giống với những gì bạn muốn làm với các hoạt động của bạn. (So ​​sánh các cụm từ tôi đã in đậm trong cả hai dấu ngoặc kép) Thay vì trả về một chuỗi, hãy trả về một mã định danh Operationtheo nghĩa trừu tượng, ví dụ, một con trỏ tới một đối tượng của lớp đó trong oop.

Về nhận xét

Nó sẽ là wtf-y để gọi lớp này là một lệnh và sau đó không có logic bên trong nó.

Không, nó sẽ không. Hãy nhớ rằng các mẫu rất trừu tượng , trừu tượng trong thực tế là chúng có phần meta. Đó là, họ thường trừu tượng hóa chính chương trình chứ không phải một số khái niệm thế giới thực. Mẫu lệnh là một sự trừu tượng của một lời gọi hàm. (hoặc phương thức) Như thể ai đó nhấn nút tạm dừng ngay sau khi chuyển các giá trị của tham số và ngay trước khi thực hiện, sẽ được tiếp tục lại sau đó.

Sau đây là xem xét oop, nhưng động lực đằng sau nó nên giữ đúng cho bất kỳ mô hình nào. Tôi muốn đưa ra một số lý do tại sao việc đặt logic vào lệnh có thể được coi là một điều xấu.

  1. Nếu bạn có tất cả logic đó trong lệnh, nó sẽ bị cồng kềnh và lộn xộn. Bạn sẽ nghĩ "ồ, tất cả chức năng này nên ở trong các lớp riêng biệt." Bạn sẽ cấu trúc lại logic ra khỏi lệnh.
  2. Nếu bạn có tất cả logic đó trong lệnh, sẽ khó kiểm tra và cản trở khi kiểm tra. "Chúa ơi, tại sao tôi phải sử dụng lệnh này vô nghĩa? Tôi chỉ muốn kiểm tra xem hàm này có nhổ ra 1 hay không, tôi không muốn gọi nó sau, tôi muốn kiểm tra ngay!"
  3. Nếu bạn có tất cả logic đó trong lệnh, sẽ không có ý nghĩa gì khi báo cáo lại khi kết thúc. Nếu bạn nghĩ về một chức năng được thực thi đồng bộ theo mặc định, thì sẽ không có nghĩa gì khi được thông báo khi hoàn thành việc thực thi. Có thể bạn đang bắt đầu một chủ đề khác bởi vì hoạt động của bạn thực sự là kết xuất hình đại diện sang định dạng rạp chiếu phim (có thể cần thêm chủ đề trên raspberry pi), có thể bạn phải lấy một số dữ liệu từ máy chủ, ... bất kể đó là gì, nếu có lý do cho báo cáo lại khi kết thúc, có thể là do có một số sự không đồng bộ (đó có phải là một từ không?) đang diễn ra. Tôi nghĩ rằng việc chạy một luồng hoặc liên hệ với một máy chủ là logic không nên có trong lệnh của bạn. Đây là một phần của một cuộc tranh luận meta và có thể gây tranh cãi. Nếu bạn nghĩ rằng nó không có ý nghĩa, xin vui lòng cho tôi biết trong các ý kiến.

Để tóm tắt lại: mẫu lệnh cho phép bạn bọc chức năng vào một đối tượng, sẽ được thực hiện sau. Vì lợi ích của tính mô đun (chức năng tồn tại bất kể được thực thi thông qua lệnh hay không), khả năng kiểm tra (chức năng nên có thể kiểm tra mà không cần lệnh) và tất cả những từ buzz khác thể hiện nhu cầu viết mã tốt, bạn sẽ không đặt logic thực tế vào lệnh.


Bởi vì các mẫu là trừu tượng, có thể khó đưa ra các ẩn dụ trong thế giới thực tốt. Đây là một nỗ lực:

"Này bà, bạn có thể vui lòng nhấn nút ghi âm trên TV lúc 12 tuổi để tôi không bỏ lỡ simpsons trên kênh 1 không?"

Bà tôi không biết điều gì xảy ra về mặt kỹ thuật khi bà nhấn nút ghi âm đó. Logic ở nơi khác (trong TV). Và đó là một điều tốt. Chức năng được gói gọn và thông tin được ẩn khỏi lệnh, nó là người dùng API, không nhất thiết phải là một phần của logic ... oh jeez Tôi đang bập bẹ những từ buzz, tôi nên hoàn thành chỉnh sửa này ngay bây giờ.


Bạn đã đúng, tôi đã không nghĩ về nó như thế. Nhưng mẫu lệnh không phù hợp 100% vì logic của hoạt động được gói gọn trong một lớp khác và tôi không thể đặt nó bên trong đối tượng là Lệnh. Sẽ là wtf-y để gọi lớp này là một lệnh và sau đó không có logic bên trong nó.
Ziv

Điều này làm cho rất nhiều ý nghĩa. Xem xét trả lại một Hoạt động, không phải là một Trình xác định hoạt động. Điều này tương tự với TPL C # nơi bạn trả lại Tác vụ hoặc Tác vụ <T>. @Ziv: Bạn nói "logic của hoạt động được gói gọn trong một lớp khác." Nhưng điều đó thực sự có nghĩa là bạn cũng không thể cung cấp nó từ đây?
Đĩa Moby

@Ziv Tôi xin khác. Hãy xem những lý do tôi thêm vào câu trả lời của tôi và xem liệu chúng có ý nghĩa gì với bạn không. =)
null

5

Ý tưởng đằng sau các kiểu nguyên thủy,

  • Để thiết lập ngôn ngữ cụ thể cho miền
  • Hạn chế lỗi do người dùng thực hiện bằng cách chuyển các giá trị không chính xác

Rõ ràng sẽ rất khó khăn và không thực tế để thực hiện ở mọi nơi, nhưng điều quan trọng là phải bọc các loại theo yêu cầu,

Ví dụ: nếu bạn có thứ tự lớp,

Order
{
   long OrderId { get; set; }
   string InvoiceNumber { get; set; }
   string Description { get; set; }
   double Amount { get; set; }
   string CurrencyCode { get; set; }
}

Các thuộc tính quan trọng để tra cứu Đơn hàng chủ yếu là OrderId và InvoiceNumber. Và Số tiền và Mã tiền tệ có liên quan chặt chẽ với nhau, trong đó nếu ai đó thay đổi Mã tiền tệ mà không thay đổi Số tiền thì Đơn hàng không còn có thể được coi là hợp lệ.

Vì vậy, chỉ gói OrderId, InvoiceNumber và giới thiệu một hỗn hợp cho tiền tệ có ý nghĩa trong kịch bản này và có lẽ mô tả gói không có ý nghĩa. Vì vậy, kết quả ưa thích có thể trông như,

    Order
    {
       OrderIdType OrderId { get; set; }
       InvoiceNumberType InvoiceNumber { get; set; }
       string Description { get; set; }
       Currency Amount { get; set; }
    }

Vì vậy, không có lý do để bọc tất cả mọi thứ, nhưng những thứ thực sự quan trọng.


4

Ý kiến ​​không phổ biến:

Bạn thường không nên xác định một loại mới!

Xác định một loại mới để bọc một lớp nguyên thủy hoặc một lớp cơ bản đôi khi được gọi là xác định loại giả. IBM mô tả đây là một thực tiễn tồi ở đây (họ tập trung đặc biệt vào việc sử dụng sai với thuốc generic trong trường hợp này).

Các loại giả làm cho chức năng thư viện phổ biến trở nên vô dụng.

Các hàm Math của Java có thể hoạt động với tất cả các nguyên hàm số. Nhưng nếu bạn xác định Tỷ lệ phần trăm lớp mới (gói gấp đôi có thể nằm trong phạm vi 0 ~ 1) thì tất cả các hàm này đều vô dụng và cần được bao bọc bởi các lớp mà (thậm chí tệ hơn) cần biết về biểu diễn bên trong của lớp Phần trăm .

Các loại giả đi virus

Khi tạo nhiều thư viện, bạn sẽ thường thấy rằng các bút danh này bị virus. Nếu bạn sử dụng lớp Tỷ lệ được đề cập ở trên trong một thư viện, bạn sẽ cần phải chuyển đổi nó ở ranh giới thư viện (mất tất cả lý do an toàn / ý nghĩa / logic / lý do khác mà bạn có để tạo loại đó) hoặc bạn sẽ phải tạo các lớp này truy cập vào thư viện khác là tốt. Lây nhiễm thư viện mới với các loại của bạn trong đó một đôi đơn giản có thể có hiệu lực.

Tin nhắn đi

Miễn là loại bạn bọc không cần nhiều logic kinh doanh, tôi sẽ khuyên bạn không nên bọc nó trong một lớp giả. Bạn chỉ nên bọc một lớp nếu có những hạn chế kinh doanh nghiêm trọng. Trong các trường hợp khác, việc đặt tên chính xác cho các biến của bạn sẽ đi một chặng đường dài trong việc truyền đạt ý nghĩa.

Một ví dụ:

A uinthoàn toàn có thể đại diện cho một UserId, chúng ta có thể tiếp tục sử dụng các toán tử tích hợp sẵn của Java cho uints (chẳng hạn như ==) và chúng ta không cần logic nghiệp vụ để bảo vệ 'trạng thái nội bộ' của id người dùng.


Đồng ý. tất cả các triết lý lập trình đều ổn, nhưng trong thực tế, bạn phải làm cho nó hoạt động tốt
Ewan

Nếu bạn đã thấy bất kỳ quảng cáo nào cho các khoản vay cho những người nghèo nhất ở Anh, bạn sẽ thấy Tỷ lệ phần trăm gấp đôi trong phạm vi 0..1 không hoạt động ...
gnasher729

1
Trong C / C ++ / Objective C, bạn có thể sử dụng typedef, cung cấp cho bạn một tên mới cho loại nguyên thủy hiện có. Mặt khác, mã của bạn sẽ hoạt động bất kể loại cơ bản nào, ví dụ nếu tôi thay đổi OrderId từ uint32_t thành uint64_t (vì tôi cần xử lý hơn 4 tỷ đơn đặt hàng).
gnasher729

Tôi sẽ không đánh giá thấp bạn vì sự dũng cảm của bạn, nhưng các kiểu giả và các loại được xác định trong câu trả lời là khác nhau. Đó là những loại hình kinh doanh cụ thể. Psuedotypes là loại vẫn còn chung chung.
0fnt

@ gnasher729: C ++ 11 cũng có nghĩa đen do người dùng định nghĩa khiến quá trình gói rất ngọt ngào cho người dùng: Khoảng cách d = 36.0_mi + 42.0_km; . msdn.microsoft.com/en-us/l
Library / dn919277.aspx

3

Như với tất cả các mẹo, biết khi nào nên áp dụng các quy tắc là kỹ năng. Nếu bạn tạo các loại của riêng bạn bằng ngôn ngữ điều khiển loại, bạn sẽ kiểm tra loại. Vì vậy, nói chung đó sẽ là một kế hoạch tốt.

Đặt tên là chìa khóa để dễ đọc. Hai kết hợp có thể truyền đạt ý định rõ ràng.
Nhưng /// vẫn hữu ích.

Vì vậy, có, tôi sẽ nói tạo nhiều loại riêng khi tuổi thọ của chúng vượt ra ngoài ranh giới lớp. Cũng xem xét việc sử dụng cả hai StructClass, không phải lúc nào cũng Class.


2
///nghĩa là gì?
Gabe

1
/// là nhận xét tài liệu trong C # cho phép intellisense hiển thị tài liệu của chữ ký tại điểm phương thức gọi. Xem xét cách tốt nhất để mã tài liệu. Có thể được nuôi bằng các công cụ và cung cấp hành vi tiếp cận nội bộ. msdn.microsoft.com/en-us/l
Library / tkxs89c5% 28v = vs.71% 29.aspx
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.