Tại sao chúng ta cần rất nhiều lớp trong các mẫu thiết kế?


209

Tôi là nhà phát triển cơ sở trong số những người cao niên và đang phải vật lộn rất nhiều với việc hiểu suy nghĩ, lý luận của họ.

Tôi đang đọc Thiết kế hướng tên miền (DDD) và không thể hiểu tại sao chúng ta cần tạo nhiều lớp như vậy. Nếu chúng ta làm theo phương pháp thiết kế phần mềm đó, chúng ta sẽ có 20-30 lớp có thể được thay thế bằng tối đa hai tệp và 3-4 hàm. Vâng, điều này có thể lộn xộn, nhưng nó dễ bảo trì và dễ đọc hơn rất nhiều.

Bất cứ khi nào tôi muốn xem một số loại EntityTransformationServiceImpllàm gì, tôi cần phải theo dõi rất nhiều lớp, giao diện, lời gọi hàm, hàm tạo, tạo của chúng, v.v.

Toán đơn giản:

  • 60 dòng mã giả so với 10 lớp X 10 (giả sử chúng ta có logic hoàn toàn khác nhau) = 600 dòng mã lộn xộn so với 100 lớp + một số chi tiết khác để bọc và quản lý chúng; đừng quên thêm phụ thuộc tiêm.
  • Đọc 600 dòng mã lộn xộn = một ngày
  • 100 lớp = một tuần, vẫn quên lớp nào làm gì, khi nào

Mọi người đều nói rằng nó dễ bảo trì, nhưng để làm gì? Mỗi khi bạn thêm chức năng mới, bạn thêm năm lớp nữa với các nhà máy, thực thể, dịch vụ và giá trị. Tôi cảm thấy như loại mã này di chuyển chậm hơn rất nhiều so với mã lộn xộn.

Giả sử, nếu bạn viết mã lộn xộn 50 nghìn LỘC trong một tháng, điều DDD yêu cầu nhiều đánh giá và thay đổi (tôi không ngại kiểm tra trong cả hai trường hợp). Một bổ sung đơn giản có thể mất tuần nếu không nhiều hơn.

Trong một năm, bạn viết rất nhiều mã lộn xộn và thậm chí có thể viết lại nhiều lần, nhưng với kiểu DDD, bạn vẫn không có đủ tính năng để cạnh tranh với mã lộn xộn.

Vui lòng giải thích. Tại sao chúng ta cần phong cách DDD này và rất nhiều mẫu?

CẬP NHẬT 1 : Tôi đã nhận được rất nhiều câu trả lời tuyệt vời, các bạn có thể vui lòng thêm nhận xét ở đâu đó hoặc chỉnh sửa câu trả lời của bạn bằng liên kết để đọc danh sách (không chắc bắt đầu từ đâu, DDD, Mẫu thiết kế, UML, Hoàn thành mã, Tái cấu trúc, Thực dụng ,. .. rất nhiều cuốn sách hay), tất nhiên với trình tự, để tôi cũng có thể bắt đầu hiểu và trở thành cấp cao như một số bạn làm.


81
Tôi nghĩ rằng có một câu hỏi hay ở đây nhưng nó ẩn đằng sau sự cường điệu và thất vọng. @ user1318496, bạn có thể hưởng lợi từ việc đọc lại câu hỏi của mình một chút.
MetaFight

48
Có nguy cơ bước lên ngón chân, bởi vì ngôn ngữ của bạn hút. Đừng coi đó là vì: ngôn ngữ sucky thường là lựa chọn kỹ thuật chính xác vì nhiều lý do, nhưng điều đó không làm cho nó trở nên không sucky. Đối với câu hỏi thực tế của bạn, tính chính xác quan trọng hơn khả năng đọc. Hoặc để nói rằng hơi khác một chút: khả năng đọc có vấn đề trong chừng mực như cho phép lý luận về tính chính xác. Vì vậy, dễ dàng hơn để kiểm tra, 100 lớp học của bạn hoặc lớp thần nguyên khối của bạn? Tôi đang đặt cược 100 lớp đơn giản.
Jared Smith

87
"Vâng, điều này có thể lộn xộn, nhưng nó dễ bảo trì và dễ đọc hơn nhiều.". Nếu nó lộn xộn, làm thế nào nó có thể đọc và bảo trì?
Polygnome

37
@Polygnome: Tôi sắp gõ cùng một bình luận. Một đặc điểm nhận xét lựa chọn khác của các nhà phát triển cơ sở là "Tôi cảm thấy như loại mã này di chuyển chậm hơn rất nhiều sau đó là mã lộn xộn". Sẽ không tốt nếu bạn chạy nhanh như bạn có thể nếu bạn dành một nửa thời gian để chạy vào tường! Chậm là trơn tru, trơn tru là nhanh chóng.
Eric Lippert

40
Bạn không, trừ khi bạn đang làm Java. Khi tất cả những gì bạn có là Java, mọi thứ trông giống như một lớp.
hobbs

Câu trả lời:


336

Đây là một vấn đề tối ưu hóa

Một kỹ sư giỏi hiểu rằng một vấn đề tối ưu hóa là vô nghĩa nếu không có mục tiêu. Bạn không thể tối ưu hóa, bạn phải tối ưu hóa cho một cái gì đó. Ví dụ: các tùy chọn trình biên dịch của bạn bao gồm tối ưu hóa tốc độ và tối ưu hóa cho kích thước mã; đôi khi chúng là những mục tiêu trái ngược nhau

Tôi muốn nói với vợ tôi rằng bàn của tôi được tối ưu hóa để thêm. Nó chỉ là một đống, và rất dễ dàng để thêm công cụ. Vợ tôi sẽ thích nó nếu tôi tối ưu hóa cho việc truy xuất, tức là sắp xếp công cụ của tôi một chút để tôi có thể tìm thấy mọi thứ. Điều này làm cho nó khó hơn, tất nhiên, để thêm.

Phần mềm là cách tương tự. Bạn chắc chắn có thể tối ưu hóa cho việc tạo sản phẩm - tạo ra một tấn mã nguyên khối càng nhanh càng tốt, mà không phải lo lắng về việc tổ chức nó. Như bạn đã nhận thấy, điều này có thể rất, rất nhanh. Cách khác là tối ưu hóa để bảo trì - làm cho việc tạo một cảm ứng khó khăn hơn, nhưng làm cho việc sửa đổi dễ dàng hơn hoặc ít rủi ro hơn. Đó là mục đích của mã cấu trúc.

Tôi muốn đề xuất rằng một sản phẩm phần mềm thành công sẽ chỉ được tạo một lần nhưng được sửa đổi nhiều, nhiều lần. Các kỹ sư giàu kinh nghiệm đã thấy các cơ sở mã không có cấu trúc có một cuộc sống của riêng họ và trở thành các sản phẩm, phát triển về quy mô và độ phức tạp, cho đến khi những thay đổi nhỏ rất khó thực hiện mà không gây ra rủi ro lớn. Nếu mã được cấu trúc, rủi ro có thể được chứa. Đó là lý do tại sao chúng ta đi đến tất cả những rắc rối này.

Sự phức tạp đến từ các mối quan hệ, không phải các yếu tố

Tôi nhận thấy trong phân tích của bạn, bạn đang xem xét số lượng - số lượng mã, số lượng lớp, v.v. Mặc dù đây là những điều thú vị, nhưng tác động thực sự đến từ mối quan hệ giữa các yếu tố, bùng nổ kết hợp. Ví dụ: nếu bạn có 10 chức năng và không có ý tưởng nào phụ thuộc vào điều đó, bạn có 90 mối quan hệ (phụ thuộc) có thể bạn phải lo lắng - mỗi trong số mười chức năng có thể phụ thuộc vào bất kỳ chức năng nào trong chín chức năng khác và 9 x 10 = 90. Bạn có thể không biết hàm nào sửa đổi biến nào hoặc cách dữ liệu được truyền qua, vì vậy các lập trình viên có rất nhiều điều phải lo lắng khi giải quyết bất kỳ vấn đề cụ thể nào. Ngược lại, nếu bạn có 30 lớp nhưng chúng được sắp xếp một cách khéo léo, chúng có thể có ít nhất 29 quan hệ, ví dụ nếu chúng được xếp lớp hoặc sắp xếp trong một ngăn xếp.

Điều này ảnh hưởng đến thông lượng của đội bạn như thế nào? Vâng, có ít phụ thuộc hơn, vấn đề dễ dàng hơn nhiều; các lập trình viên không phải tung hứng hàng trăm thứ trong đầu mỗi khi họ thay đổi. Vì vậy, giảm thiểu sự phụ thuộc có thể là một sự thúc đẩy to lớn cho khả năng suy luận về vấn đề của bạn. Đó là lý do tại sao chúng tôi chia mọi thứ thành các lớp hoặc mô-đun và phạm vi biến càng chặt chẽ càng tốt và sử dụng các nguyên tắc RẮN .


103
Tuy nhiên, tôi sẽ lưu ý rằng sự dư thừa các lớp học và sự gián tiếp KHÔNG giúp hiểu được sự bảo trì của NOR. Và cũng không có dòng điều khiển phức tạp (hay còn gọi là địa ngục gọi lại).
Matthieu M.

9
@ John John giải thích này là cách tốt nhất và dễ hiểu hơn mà tôi từng đọc.
Adriano Repetti

13
Được viết tốt, nhưng nó có thể thiếu tại sao "được tạo một lần nhưng sửa đổi nhiều lần, nhiều lần" lại quan trọng? (Nếu tất cả mã nằm trong EntityTransformationServiceImpl, bạn buộc phải tìm hiểu cách thức toàn bộ hoạt động trước khi bạn có thể sửa nó - nhưng nếu ví dụ có gì đó không đúng với định dạng và một Formatterlớp xử lý điều đó, bạn chỉ cần tìm hiểu cách phần đó hoạt động . đọc mã của riêng bạn sau ~ 3 tháng giống như đọc mã của người lạ.) Tôi cảm thấy như hầu hết chúng ta đều nghĩ về điều này trong tiềm thức, nhưng đó có thể là do kinh nghiệm. Không chắc chắn 100% về điều này.
R. Schmitz

17
Tôi muốn sử dụng toàn bộ 10 × 10 = 100 cho tổ hợp: các hàm đó có thể được đệ quy và trong mã đặc biệt kém, rõ ràng là không rõ ràng như vậy.
KRyan

32
"Giải pháp thay thế là tối ưu hóa để bảo trì - làm cho việc tạo ra một cảm ứng khó khăn hơn, nhưng làm cho việc sửa đổi dễ dàng hơn hoặc ít rủi ro hơn. Đó là mục đích của mã có cấu trúc." Tôi có vấn đề với khái niệm ngầm rằng nhiều lớp hơn = khả năng bảo trì nhiều hơn. Cụ thể, logic phân tán trên nhiều lớp thường gây khó khăn cho việc tìm các khái niệm logic bao quát trong mã (đặc biệt là không có con mắt rất có chủ ý trong việc đảm bảo các khái niệm rất rõ ràng), làm giảm khả năng duy trì rất lớn.
jpmc26

67

Vâng, trước hết, khả năng đọc và bảo trì thường trong mắt của kẻ si tình.

Những gì có thể đọc được cho bạn có thể không phải là hàng xóm của bạn.

Khả năng bảo trì thường tập trung vào khả năng khám phá (một hành vi hoặc khái niệm được phát hiện trong codebase dễ dàng như thế nào) và khả năng khám phá là một điều chủ quan khác.

DDD

Một trong những cách DDD giúp các nhóm các nhà phát triển là bằng cách đề xuất một cách cụ thể (nhưng vẫn chủ quan) để tổ chức các khái niệm và hành vi mã của bạn. Quy ước này giúp cho việc khám phá mọi thứ dễ dàng hơn và do đó dễ dàng duy trì ứng dụng hơn.

  • Các khái niệm miền được mã hóa dưới dạng Thực thể và Tập hợp
  • Hành vi tên miền nằm trong Thực thể hoặc Dịch vụ Miền
  • Tính nhất quán được đảm bảo bởi Rễ tổng hợp
  • Mối quan tâm dai dẳng được xử lý bởi Kho

Sự sắp xếp này không khách quan dễ dàng hơn để duy trì. Đó là, tuy nhiên, cách đo dễ dàng hơn để duy trì khi mọi người hiểu họ đang hoạt động trong một bối cảnh DDD.

Các lớp học

Các lớp học giúp duy trì, dễ đọc, có thể khám phá, vv ... bởi vì chúng là một quy ước nổi tiếng .

Trong cài đặt Hướng đối tượng, Các lớp thường được sử dụng để nhóm hành vi liên quan chặt chẽ và đóng gói trạng thái cần được kiểm soát cẩn thận.

Tôi biết rằng âm thanh rất trừu tượng, nhưng bạn có thể nghĩ về nó theo cách này:

Với các lớp, bạn không nhất thiết phải biết cách mã trong chúng hoạt động. Bạn chỉ cần biết những gì lớp chịu trách nhiệm.

Các lớp học cho phép bạn suy luận về ứng dụng của bạn về mặt tương tác giữa các thành phần được xác định rõ .

Điều này làm giảm gánh nặng nhận thức khi suy luận về cách ứng dụng của bạn hoạt động. Thay vì phải nhớ 600 dòng mã hoàn thành, bạn có thể suy nghĩ về cách 30 thành phần tương tác.

Và, xem xét 30 thành phần đó có thể trải rộng 3 lớp trong ứng dụng của bạn, có lẽ bạn chỉ phải suy luận về khoảng 10 thành phần một lần.

Điều đó có vẻ khá dễ quản lý.

Tóm lược

Về cơ bản, những gì bạn đang thấy các nhà phát triển cấp cao làm là:

Họ đang chia nhỏ ứng dụng thành lý do dễ dàng về các lớp học.

Sau đó, họ đang tổ chức những điều này thành dễ dàng để lý luận về các lớp.

Họ đang làm điều này bởi vì họ biết rằng, khi ứng dụng phát triển, nó càng trở nên khó hơn và khó hơn để suy luận về nó. Chia nó thành các Lớp và Lớp có nghĩa là họ không bao giờ phải suy luận về toàn bộ ứng dụng. Họ chỉ cần lý do về một tập hợp con nhỏ của nó.


5
Bên cạnh đó các lớp nhỏ dễ dàng hơn để thay thế / tái cấu trúc.
Zalomon

12
Overengineering không cung cấp mã dễ bảo trì hoặc dễ hiểu hơn - hoàn toàn ngược lại, bất chấp những gì bạn yêu cầu. Ai cần một AddressServiceRepositoryProxyInjectorResolverkhi bạn có thể giải quyết vấn đề một cách thanh lịch hơn? Các mẫu thiết kế vì lợi ích của các mẫu thiết kế chỉ dẫn đến sự phức tạp và dài dòng không cần thiết.
vikingsteve

27
Vâng, áp đảo là xấu. Giết chó con cũng vậy. Không ai ở đây là ủng hộ cho một trong hai. logicallyfallacious.com/tools/lp/Bo/LogicalFallacies/169/...
MetaFight

5
Ngoài ra, "sự tao nhã" trong ngữ cảnh công nghệ phần mềm thường là Weasel Word. vi.wikipedia.org/wiki/Weasel_word
MetaFight

11
Điều tương tự có thể được nói về bất kỳ cách tiếp cận nào để tổ chức mã. Mọi thứ bản lề trong nhóm bảo trì đều hiểu tại sao mã được tổ chức như hiện tại. Đó là toàn bộ quan điểm của việc sử dụng các quy ước được thiết lập và hiểu rõ.
MetaFight

29

Hãy giải thích cho tôi, tại sao chúng ta cần phong cách DDD này, rất nhiều Mẫu?

Đầu tiên, một lưu ý: phần quan trọng của DDD không phải là các mẫu , mà là sự liên kết của nỗ lực phát triển với doanh nghiệp. Greg Young nhận xét rằng các chương trong cuốn sách màu xanh là sai thứ tự .

Nhưng với câu hỏi cụ thể của bạn: có xu hướng có nhiều lớp hơn bạn mong đợi, (a) vì một nỗ lực đang được thực hiện để phân biệt hành vi miền với hệ thống ống nước và (b) vì nỗ lực thêm được thực hiện để đảm bảo rằng các khái niệm trong mô hình miền được thể hiện rõ ràng.

Nói một cách thẳng thắn, nếu bạn có hai khái niệm khác nhau trong miền, thì chúng phải khác biệt trong mô hình ngay cả khi chúng tình cờ chia sẻ giống nhau trong biểu diễn bộ nhớ .

Trên thực tế, bạn đang xây dựng một ngôn ngữ cụ thể cho miền mô tả mô hình của bạn bằng ngôn ngữ của doanh nghiệp, sao cho một chuyên gia tên miền có thể xem xét nó và phát hiện ra các lỗi.

Ngoài ra, bạn thấy chú ý hơn một chút để phân tách mối quan tâm; và khái niệm của người tiêu dùng cách điện một số khả năng từ các chi tiết thực hiện. Xem Parnas DL . Các ranh giới rõ ràng cho phép bạn thay đổi hoặc mở rộng việc thực hiện mà không có các hiệu ứng gợn sóng trong toàn bộ giải pháp của bạn.

Động lực ở đây: đối với một ứng dụng là một phần của năng lực cốt lõi của một doanh nghiệp (nghĩa là một nơi có lợi thế cạnh tranh), bạn sẽ muốn có thể dễ dàng và rẻ tiền thay thế một hành vi tên miền bằng một biến thể tốt hơn. Trên thực tế, bạn có các phần của chương trình mà bạn muốn phát triển nhanh chóng (cách trạng thái phát triển theo thời gian) và các phần khác mà bạn muốn thay đổi chậm (cách lưu trữ trạng thái); các lớp trừu tượng thêm giúp tránh vô tình ghép cái này với cái kia.

Nói một cách công bằng: một số trong đó cũng là tổn thương não hướng đối tượng. Các mô hình ban đầu được mô tả bởi Evans dựa trên các dự án Java mà ông đã tham gia từ hơn 15 năm trước; trạng thái và hành vi được kết hợp chặt chẽ theo phong cách đó, dẫn đến các biến chứng bạn có thể muốn tránh; xem Nhận thức và hành động của Stuart Halloway hoặc Mã nội tuyến của John Carmack.

Bất kể bạn làm việc với ngôn ngữ nào, lập trình theo phong cách chức năng đều mang lại lợi ích. Bạn nên làm điều đó bất cứ khi nào thuận tiện và bạn nên suy nghĩ kỹ về quyết định khi nó không thuận tiện. Xe cộ, 2012


2
Tôi sẽ thêm câu trả lời của riêng mình cho đến khi tôi đọc nó. Tôi sẽ nhấn mạnh đoạn đầu tiên, rằng mục đích là mô hình hóa các khái niệm kinh doanh trong phần mềm để phù hợp với phần mềm với các yêu cầu kinh doanh. Nói một cách công bằng, nhà phân tích điều khiển dự án cũng nên thể hiện thời gian giao hàng của tài xế là bao nhiêu và chất lượng mã / kiểm tra hoặc tính đầy đủ phải được điều chỉnh cho phù hợp.
Sentinel

1
John Carmack là một ví dụ tuyệt vời IMO, vì anh ta nổi tiếng với việc viết mã vừa sạch sẽ, dễ hiểu, vừa hoạt động tốt. Điều này đặc biệt rõ ràng khi bạn theo dõi mã của anh ấy bằng các công nghệ tiên tiến - với CPU và bộ nhớ tốt hơn, bạn có thể thấy rất nhiều thứ được chuyển từ "điều này cần phải thực sự ngắn gọn" thành "điều này cần phải thực sự rõ ràng / cô lập / ... ". Một trong những lập trình viên thực dụng nhất mà tôi biết :)
Luaan

Tôi nghĩ rằng đây là câu trả lời tốt nhất ở đây. Mặc dù tôi không bao giờ được bán trên DDD, nhưng câu trả lời này chắc chắn ít nhất giải thích nó thực sự là gì và một động lực thực sự cho nó, thay vì hấp dẫn các quy mô thông thường không có nghĩa gì.
jpmc26

29

Có nhiều điểm hay trong các câu trả lời khác, nhưng tôi nghĩ rằng họ bỏ lỡ hoặc không nhấn mạnh một lỗi khái niệm quan trọng mà bạn mắc phải:

Bạn đang so sánh nỗ lực để hiểu chương trình hoàn chỉnh.

Đây không phải là một nhiệm vụ thực tế với hầu hết các chương trình. Ngay cả các chương trình đơn giản bao gồm rất nhiều mã đến mức đơn giản là không thể quản lý tất cả mã trong đầu tại bất kỳ thời điểm nào. Cơ hội duy nhất của bạn là tìm một phần của chương trình có liên quan đến nhiệm vụ hiện tại (sửa lỗi, thực hiện một tính năng mới) và làm việc với nó.

Nếu chương trình của bạn bao gồm các hàm / phương thức / lớp lớn thì điều này gần như không thể. Bạn sẽ phải hiểu hàng trăm dòng mã chỉ để quyết định xem đoạn mã này có liên quan đến vấn đề của bạn không. Với các ước tính bạn đã đưa ra, thật dễ dàng để dành một tuần chỉ để tìm đoạn mã bạn cần để làm việc.

So sánh điều đó với một cơ sở mã với hàm / phương thức / lớp nhỏ được đặt tên và sắp xếp thành các gói / không gian tên làm cho nó rõ ràng ở đâu để tìm / đặt một đoạn logic nhất định. Khi được thực hiện đúng trong nhiều trường hợp, bạn có thể nhảy ngay đến đúng nơi để giải quyết vấn đề của mình hoặc ít nhất là đến một nơi mà việc khởi động trình gỡ lỗi của bạn sẽ đưa bạn đến đúng vị trí trong một vài bước nhảy.

Tôi đã làm việc trong cả hai loại hệ thống. Sự khác biệt có thể dễ dàng là hai bậc độ lớn trong hiệu suất cho các nhiệm vụ tương đương và kích thước hệ thống tương đương.

Ảnh hưởng này đến các hoạt động khác:

  • kiểm tra trở nên dễ dàng hơn nhiều với các đơn vị nhỏ hơn
  • ít xung đột hợp nhất vì cơ hội cho hai nhà phát triển làm việc trên cùng một đoạn mã nhỏ hơn.
  • ít trùng lặp vì dễ sử dụng lại các mảnh (và để tìm các mảnh ở vị trí đầu tiên).

19

Bởi vì mã kiểm tra khó hơn viết mã

Rất nhiều câu trả lời đã đưa ra lý do chính đáng từ quan điểm của nhà phát triển - rằng việc bảo trì có thể được giảm bớt, với chi phí làm cho mã trở nên vất vả hơn để viết ngay từ đầu.

Tuy nhiên, có một khía cạnh khác để xem xét - thử nghiệm chỉ có thể được xử lý tốt như mã gốc của bạn.

Nếu bạn viết mọi thứ trong một khối, bài kiểm tra hiệu quả duy nhất bạn có thể viết là "đưa ra các đầu vào này, đầu ra có đúng không?". Điều này có nghĩa là bất kỳ lỗi nào được tìm thấy, đều nằm trong phạm vi "đâu đó trong đống mã khổng lồ" đó.

Tất nhiên, sau đó bạn có thể có một nhà phát triển ngồi với trình gỡ lỗi và tìm chính xác nơi sự cố bắt đầu và khắc phục. Điều này tốn rất nhiều tài nguyên và sử dụng không tốt thời gian của nhà phát triển. Hãy tưởng tượng rằng bạn từng có một lỗi nhỏ, dẫn đến việc nhà phát triển cần gỡ lỗi lại toàn bộ chương trình.

Giải pháp: Nhiều thử nghiệm nhỏ hơn xác định chính xác từng thất bại cụ thể.

Các thử nghiệm nhỏ này (ví dụ: Thử nghiệm đơn vị), có lợi thế là kiểm tra một khu vực cụ thể của cơ sở mã và giúp tìm ra các lỗi trong phạm vi giới hạn. Điều này không chỉ tăng tốc độ gỡ lỗi khi thử nghiệm thất bại, mà còn có nghĩa là nếu tất cả các thử nghiệm nhỏ của bạn đều thất bại - bạn có thể dễ dàng tìm thấy lỗi trong các thử nghiệm lớn hơn của mình (nghĩa là nếu nó không nằm trong một chức năng được thử nghiệm cụ thể, thì nó phải nằm trong tương tác giữa họ).

Cần phải rõ ràng, để thực hiện các thử nghiệm nhỏ hơn có nghĩa là cơ sở mã của bạn cần được chia thành các phần có thể kiểm tra nhỏ hơn. Cách để làm điều đó, trên một cơ sở mã thương mại lớn, thường dẫn đến mã trông giống như những gì bạn đang làm việc.

Cũng như một lưu ý phụ: Điều này không có nghĩa là mọi người sẽ không đưa mọi thứ "quá xa". Nhưng có một lý do chính đáng để tách các cơ sở mã thành các phần nhỏ hơn / ít kết nối hơn - nếu được thực hiện hợp lý.


5
Mặc dù câu trả lời của bạn chỉ đề cập đến kiểm tra và kiểm tra, nhưng đó là vấn đề quan trọng nhất mà một số câu trả lời khác hoàn toàn bỏ qua nên +1.
skomisa

17

Hãy giải thích cho tôi, tại sao chúng ta cần phong cách DDD này, rất nhiều Mẫu?

Nhiều người (hầu hết ...) thực sự không cần chúng. Các nhà lý thuyết và các lập trình viên rất có kinh nghiệm, rất tiên tiến viết sách về các lý thuyết và phương pháp luận là kết quả của rất nhiều nghiên cứu và kinh nghiệm sâu sắc của họ - điều đó không có nghĩa là mọi thứ họ viết đều có thể áp dụng cho mọi lập trình viên trong thực tiễn hàng ngày.

Là một nhà phát triển cơ sở, thật tốt khi đọc những cuốn sách như cuốn sách mà bạn đã đề cập để mở rộng quan điểm của bạn và giúp bạn nhận thức được một số vấn đề nhất định. Nó cũng sẽ giúp bạn không bị bối rối và bối rối khi các đồng nghiệp cấp cao của bạn sử dụng thuật ngữ mà bạn không quen thuộc. Nếu bạn thấy điều gì đó rất khó khăn và dường như không có ý nghĩa hoặc có vẻ hữu ích, đừng tự giết mình - hãy ghi lại trong đầu rằng có một khái niệm hoặc cách tiếp cận như vậy.

Trong sự phát triển hàng ngày của bạn, trừ khi bạn là một học giả, công việc của bạn là tìm các giải pháp khả thi, có thể duy trì. Nếu những ý tưởng bạn tìm thấy trong một cuốn sách không giúp bạn đạt được mục tiêu đó, thì đừng lo lắng về nó ngay bây giờ, miễn là công việc của bạn được coi là thỏa đáng.

Sẽ có lúc bạn phát hiện ra rằng bạn có thể sử dụng một số nội dung bạn đọc nhưng ban đầu không "hiểu" hoặc có thể không.


12
Mặc dù ở mức độ thuần túy, điều này đúng, có vẻ như DDD và các Mẫu khác chỉ là lý thuyết. Họ không. Chúng là các công cụ quan trọng để đạt được mã có thể đọc và duy trì.
Jens Schauder

9
@PeteKirkham tiến thoái lưỡng nan của OP là sự lạm dụng các mẫu thiết kế. Bạn có thực sự cần một AccountServiceFacadeInjectResolver(ví dụ thực tế tôi vừa tìm thấy trong một hệ thống tại nơi làm việc) - câu trả lời có lẽ là không.
vikingsteve

4
@vikingsteve OP không phải là một chuyên gia lập trình bình luận về việc lạm dụng chung các mẫu thiết kế. OP là người học việc và nghĩ rằng họ bị lạm dụng tại cửa hàng của mình . Tôi biết rằng người mới bắt đầu cũng sẽ nghĩ giống như vậy về mã tôi viết ngày nay, nhưng người mới bắt đầu tôi đã có rất nhiều dự án cá nhân thất bại vì cuối cùng họ đã quá bối rối.
R. Schmitz

3
@ R.Schmitz Điều đó nói rằng, người mới bắt đầu tôi đã thất bại rất nhiều dự án cá nhân vì họ đã cố gắng quá nhiều để làm cho mọi thứ được thiết kế tốt, có tổ chức, có cấu trúc, OOP ... Tất cả đều là những công cụ. Chúng cần được hiểu và sử dụng đúng cách, và bạn khó có thể đổ lỗi cho búa vì kém trong việc bắt vít. Đáng ngạc nhiên, dường như cần rất nhiều hiểu biết và kinh nghiệm để hiểu điều đơn giản này :)
Luaan

3
@Luaan Nếu bạn đã quan tâm đến thiết kế tốt, có tổ chức, có cấu trúc, OOP, tôi hầu như không gọi người mới bắt đầu đó, nhưng vâng, lạm dụng là xấu. Tuy nhiên, câu hỏi là về cách OP không thực sự hiểu những vấn đề mà những điều đó giải quyết. Nếu bạn không biết lý do, có vẻ như không có lý do - nhưng thực ra, bạn chưa biết điều đó.
R. Schmitz

8

Vì câu hỏi của bạn bao trùm rất nhiều nền tảng, với rất nhiều giả định, tôi sẽ bỏ qua chủ đề câu hỏi của bạn:

Tại sao chúng ta cần rất nhiều lớp trong các mẫu thiết kế

Chúng ta không. Không có quy tắc được chấp nhận chung nói rằng phải có nhiều lớp trong các mẫu thiết kế.

Có hai hướng dẫn chính để quyết định nơi đặt mã và cách cắt các tác vụ của bạn thành các đơn vị mã khác nhau:

  • Sự gắn kết: bất kỳ đơn vị mã nào (có thể là một gói, một tệp, một lớp hoặc một phương thức) nên thuộc về nhau . Tức là, bất kỳ phương pháp cụ thể nào cũng nên có một nhiệm vụ và thực hiện tốt nhiệm vụ đó. Bất kỳ lớp học nào cũng phải chịu trách nhiệm cho một chủ đề lớn hơn (bất cứ điều gì có thể). Chúng tôi muốn sự gắn kết cao.
  • Khớp nối: bất kỳ hai đơn vị mã nào nên phụ thuộc ít nhất vào nhau càng tốt - đặc biệt không nên có phụ thuộc vòng tròn. Chúng tôi muốn khớp nối thấp.

Tại sao hai điều đó nên quan trọng?

  • Sự gắn kết: một phương thức thực hiện rất nhiều thứ (ví dụ: tập lệnh CGI kiểu cũ có GUI, logic, truy cập DB, v.v ... tất cả trong một mớ hỗn độn mã dài) trở nên khó sử dụng. Tại thời điểm viết, thật hấp dẫn khi chỉ đưa ý nghĩ của bạn vào một phương pháp dài. Điều này hoạt động, nó dễ dàng để trình bày và như vậy, và bạn có thể được thực hiện với nó. Rắc rối phát sinh sau đó: sau một vài tháng, bạn có thể quên những gì bạn đã làm. Một dòng mã ở trên cùng có thể là một vài màn hình cách một dòng ở phía dưới; thật dễ dàng để quên tất cả các chi tiết. Bất kỳ thay đổi bất cứ nơi nào trong phương pháp có thể phá vỡ bất kỳ số lượng hành vi của điều phức tạp. Nó sẽ khá cồng kềnh để tái cấu trúc hoặc sử dụng lại các phần của phương thức. Và như vậy.
  • Khớp nối: bất cứ khi nào bạn thay đổi một đơn vị mã, bạn có khả năng phá vỡ tất cả các đơn vị khác phụ thuộc vào nó. Trong các ngôn ngữ nghiêm ngặt như Java, bạn có thể nhận được gợi ý trong thời gian biên dịch (nghĩa là về các tham số, ngoại lệ được khai báo, v.v.). Nhưng nhiều thay đổi không kích hoạt như vậy (tức là thay đổi hành vi) và các ngôn ngữ khác, năng động hơn, không có khả năng như vậy. Khớp nối càng cao, càng khó thay đổi bất cứ điều gì, và bạn có thể dừng lại, trong đó việc viết lại hoàn toàn là cần thiết để đạt được một số mục tiêu.

Hai khía cạnh này là "trình điều khiển" cơ bản cho bất kỳ lựa chọn "đặt cái gì" trong bất kỳ ngôn ngữ lập trình nào và bất kỳ mô hình nào (không chỉ OO). Không phải ai cũng hiểu rõ về chúng, và phải mất nhiều thời gian, thường là nhiều năm, để có được cảm giác thực sự ăn sâu, tự động về cách những phần mềm ảnh hưởng này.

Rõ ràng, hai khái niệm đó không cho bạn biết bất cứ điều gì về những gì thực sự phải làm . Một số người sai về phía quá nhiều, những người khác ở phía quá ít. Một số ngôn ngữ (nhìn vào bạn ở đây, Java) có xu hướng ủng hộ nhiều lớp vì bản chất cực kỳ tĩnh và mang tính mô phạm của chính ngôn ngữ này (đây không phải là một tuyên bố giá trị, nhưng nó là như vậy). Điều này trở nên đặc biệt đáng chú ý khi bạn so sánh nó với các ngôn ngữ năng động và biểu cảm hơn, ví dụ như Ruby.

Một khía cạnh khác là một số người đăng ký theo cách tiếp cận nhanh nhẹn chỉ viết mã cần thiết ngay bây giờ và để cấu trúc lại rất nhiều sau này, khi cần thiết. Trong phong cách phát triển này, bạn sẽ không tạo ra interfacekhi bạn chỉ có một lớp thực hiện. Bạn chỉ cần thực hiện các lớp cụ thể. Nếu, sau này, bạn cần một lớp thứ hai, bạn sẽ tái cấu trúc.

Một số người chỉ đơn giản là không làm việc theo cách đó. Chúng tạo ra các giao diện (hoặc, nói chung hơn, các lớp cơ sở trừu tượng) cho bất kỳ thứ gì có thể được sử dụng chung hơn; Điều này dẫn đến một vụ nổ lớp nhanh chóng.

Một lần nữa, có những tranh luận cho và chống lại, và điều đó không quan trọng mà tôi, hoặc bạn, thích. Trong cuộc đời của bạn, với tư cách là nhà phát triển phần mềm, sẽ gặp phải tất cả các thái cực, từ các phương pháp spaghetti dài, thông qua các thiết kế lớp vừa đủ lớn, vừa sáng suốt, cho đến các kế hoạch lớp vô cùng khó chịu, bị áp đảo. Khi bạn có nhiều kinh nghiệm hơn, bạn sẽ phát triển nhiều hơn thành vai trò "kiến trúc" và có thể bắt đầu ảnh hưởng đến điều này theo hướng bạn muốn. Bạn sẽ tìm ra một trung gian vàng cho chính mình, và bạn vẫn sẽ thấy rằng nhiều người sẽ không đồng ý với bạn, bất cứ điều gì bạn làm.

Do đó, giữ một tâm trí cởi mở là điều quan trọng nhất, ở đây, và đó sẽ là lời khuyên chính của tôi cho bạn, khi thấy rằng bạn dường như rất đau đớn về vấn đề này, đánh giá bằng phần còn lại của câu hỏi của bạn ...


7

Các lập trình viên giàu kinh nghiệm đã học được:

  • Bởi vì những chương trình nhỏ và clasess bắt đầu phát triển, đặc biệt là những chương trình thành công. Đó là các mẫu đơn giản làm việc ở cấp độ đơn giản không quy mô.
  • Bởi vì nó có vẻ nặng nề khi phải thêm / thay đổi một số tạo phẩm cho mỗi lần thêm / thay đổi nhưng bạn biết phải thêm gì và rất dễ để làm như vậy. 3 phút gõ nhịp 3 giờ mã hóa thông minh.
  • Rất nhiều lớp học nhỏ không "lộn xộn" chỉ vì bạn không hiểu tại sao chi phí thực sự là một ý tưởng hay
  • Không có kiến ​​thức tên miền được thêm vào, mã 'rõ ràng đối với tôi' thường bí ẩn đối với các đồng đội của tôi ... cộng với tương lai của tôi.
  • Kiến thức bộ lạc có thể làm cho các dự án cực kỳ khó khăn để thêm các thành viên trong nhóm để dễ dàng và khó để họ có thể làm việc nhanh chóng.
  • Đặt tên là một trong hai vấn đề khó khăn trong điện toán vẫn còn đúng và rất nhiều lớp học và phương pháp thường là một bài tập lớn trong việc đặt tên mà nhiều người cho là có lợi lớn.

5
Nhưng là chi phí cần thiết? Trong nhiều trường hợp mà tôi thấy, các mẫu thiết kế bị lạm dụng. Kết quả của sự không tuân thủ là sự khó khăn trong việc hiểu, duy trì, kiểm tra và gỡ lỗi mã. Khi bạn có 12 lớp và 4 mô-đun maven xung quanh một lớp dịch vụ đơn giản (một ví dụ thực tế tôi vừa thấy), nó sẽ nhanh chóng trở nên ít quản lý hơn bạn nghĩ.
vikingsteve

4
@vikingsteve Bạn tiếp tục đề cập đến một ví dụ duy nhất về việc triển khai thiếu sót với hy vọng rằng nó sẽ chứng minh rằng mã có cấu trúc, nói chung, là một ý tưởng tồi. Điều đó không theo dõi.
MetaFight

2
@MetaFight OP không nói về mã cấu trúc. Anh ấy đang nói về, những gì anh ấy thấy, quá nhiều trừu tượng. Nếu bạn có quá nhiều trừu tượng, tôi lập luận, bạn sẽ nhanh chóng nhận được mã rất không có cấu trúc và rất nhiều phần gần giống nhau, đơn giản là vì mọi người không thể đối phó với số lượng công cụ họ phải xử lý.
Rõ ràng hơn

4
@Clearer Tôi khá chắc chắn rằng tất cả mọi người ở đây đều đồng ý rằng việc áp dụng sai mã giả định của mã có cấu trúc là một điều xấu. OP nói họ là Junior. Đó là lý do tại sao mọi người cho rằng anh ấy / cô ấy không quen thuộc với những lợi ích của mã có cấu trúc và nhắc lại chúng.
MetaFight

2

Các câu trả lời cho đến nay, tất cả đều tốt, đã bắt đầu với giả định hợp lý rằng người hỏi đang thiếu một cái gì đó, điều mà người hỏi cũng thừa nhận. Cũng có thể người hỏi về cơ bản là đúng, và đáng để thảo luận về cách tình huống này có thể xảy ra.

Kinh nghiệm và thực tiễn là mạnh mẽ, và nếu người cao niên có được kinh nghiệm trong các dự án lớn, phức tạp, cách duy nhất để giữ mọi thứ trong tầm kiểm soát là nhiều EntityTransformationServiceImpl, thì họ trở nên nhanh chóng và thoải mái với các mẫu thiết kế và tuân thủ chặt chẽ DDD. Chúng sẽ kém hiệu quả hơn nhiều khi sử dụng phương pháp nhẹ, ngay cả đối với các chương trình nhỏ. Là một trong những lẻ, bạn nên thích nghi và nó sẽ là một kinh nghiệm học tập tuyệt vời.

Mặc dù thích nghi, bạn nên xem nó như một bài học về sự cân bằng giữa việc học một cách tiếp cận sâu sắc, đến mức bạn có thể làm cho nó hoạt động ở bất cứ đâu, thay vì linh hoạt và biết công cụ nào có sẵn mà không nhất thiết phải là chuyên gia với bất kỳ ai trong số họ. Có những lợi thế cho cả hai và cả hai đều cần thiết trên thế giới.


-4

Tạo thêm lớp và chức năng theo mỗi lần sử dụng sẽ là một cách tiếp cận tuyệt vời để giải quyết các vấn đề theo cách cụ thể giúp giải quyết mọi vấn đề trong tương lai.

Nhiều lớp giúp xác định là công việc cơ bản của họ và bất kỳ lớp nào cũng có thể được gọi bất cứ lúc nào.

Tuy nhiên, nếu bạn có nhiều lớp và chức năng từ tên có thể nhận được của họ, họ sẽ dễ dàng gọi và quản lý. Đây được gọi là mã sạch.


9
Xin lỗi, nhưng bạn đang nói gì vậy?
Ded repeatator

@Ded repeatator hãy lấy một ví dụ trong java nếu chúng ta thiết kế chúng ta đã sử dụng các chủ đề và các lớp jframes cho sự kế thừa của chúng. Chỉ từ một lớp, chúng tôi không thể sử dụng một số tác phẩm của java để thiết kế, vì vậy chúng tôi cần tạo thêm các lớp
Sanjeev Chauhan

2
Câu trả lời này cần trình bày (các) ý tưởng rằng nó có ý định trình bày bằng tiếng Anh rõ ràng hơn. Có vẻ như ý tưởng cốt lõi trong câu trả lời là các lớp nhỏ hơn, mỗi lớp có một chức năng được xác định rõ, là một ý tưởng tốt, nhưng ngữ pháp khủng khiếp khiến nó gần như không thể hiểu được. Ngoài ra, tự nó, khẳng định này có thể không đủ.
Talonx
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.