Điều gì nên được ưu tiên: YAGNI hoặc Thiết kế tốt?


76

Tại thời điểm nào YAGNI nên được ưu tiên chống lại các hoạt động mã hóa tốt và ngược lại? Tôi đang làm việc trong một dự án tại nơi làm việc và muốn từ từ giới thiệu các tiêu chuẩn mã tốt cho đồng nghiệp của mình (hiện tại không có gì và mọi thứ chỉ là bị hack với nhau mà không có vần điệu hoặc lý do), nhưng sau khi tạo ra một loạt các lớp (chúng tôi không làm TDD, hoặc đáng buồn là bất kỳ loại thử nghiệm đơn vị nào) Tôi lùi lại một bước và nghĩ rằng nó vi phạm YAGNI vì tôi chắc chắn biết rằng chúng tôi không cần phải mở rộng một số lớp này.

Đây là một ví dụ cụ thể về ý tôi muốn nói: Tôi có một lớp truy cập dữ liệu bao gồm một tập các thủ tục được lưu trữ, sử dụng mẫu kiểu Kho lưu trữ thô sơ với các hàm CRUD cơ bản. Vì có một số phương thức mà tất cả các lớp kho lưu trữ của tôi cần, tôi đã tạo một giao diện chung cho kho lưu trữ của mình, được gọi là IRepository. Tuy nhiên, sau đó tôi đã tạo một giao diện "đánh dấu" (tức là giao diện không thêm bất kỳ chức năng mới nào) cho từng loại kho lưu trữ (ví dụ ICustomerRepository) và lớp cụ thể thực hiện điều đó. Tôi đã làm điều tương tự với việc triển khai Factory để xây dựng các đối tượng kinh doanh từ DataReaders / DataSets được trả về bởi Thủ tục lưu trữ; chữ ký của lớp kho lưu trữ của tôi có xu hướng trông giống như thế này:

public class CustomerRepository : ICustomerRepository
{
    ICustomerFactory factory = null;

    public CustomerRepository() : this(new CustomerFactory() { }

    public CustomerRepository(ICustomerFactory factory) {
        this.factory = factory;
    }      

    public Customer Find(int customerID)
    {
        // data access stuff here
        return factory.Build(ds.Tables[0].Rows[0]);
    }
}

Mối quan tâm của tôi ở đây là tôi vi phạm YAGNI bởi vì tôi biết chắc chắn 99% rằng sẽ không bao giờ có lý do để đưa ra bất cứ điều gì ngoài việc cụ thể CustomerFactorycho kho lưu trữ này; vì chúng tôi không có bài kiểm tra đơn vị nên tôi không cần một MockCustomerFactorythứ tương tự và việc có quá nhiều giao diện có thể khiến đồng nghiệp bối rối. Mặt khác, sử dụng một triển khai cụ thể của nhà máy có vẻ như một mùi thiết kế.

Có một cách tốt để đi đến một sự thỏa hiệp giữa thiết kế phần mềm phù hợp và không bao quát kiến ​​trúc giải pháp? Tôi đang đặt câu hỏi liệu tôi có cần phải có tất cả "giao diện cấy ghép đơn" hay không nếu tôi có thể hy sinh một chút thiết kế tốt và ví dụ như giao diện cơ bản và sau đó là bê tông đơn lẻ, và không lo lắng về việc lập trình cho giao diện nếu việc thực hiện sẽ được sử dụng.


17
Bạn nói "vì chúng tôi không có bài kiểm tra đơn vị nên tôi không cần MockX", điều này dẫn đến "Tôi không cần IX, tôi chỉ cần X". Tôi lật lại, thực tế là bạn không có bài kiểm tra đơn vị nêu bật thực tế rằng bạn cần IX và MockX, bởi vì những điều này sẽ giúp bạn bài kiểm tra đơn vị. Đừng chấp nhận thực tế là không có xét nghiệm, coi đó là một vấn đề tạm thời sẽ được khắc phục trong thời gian dài (có lẽ là tốt, lâu dài).
Anthony Pegram

10
Mặc dù nó không quan trọng đối với google, nhưng ai đó nên đề cập rằng YAGNI là viết tắt của "Bạn sẽ không cần nó"
thedaian

1
Tôi nghĩ rằng nếu bạn đang viết các lớp mới như thế này, bạn sẽ muốn thêm một số bài kiểm tra đơn vị. Ngay cả khi đồng nghiệp của bạn sẽ không chạy chúng. Ít nhất sau này bạn có thể nói, "Hãy nhìn xem! Các bài kiểm tra đơn vị của tôi đã bắt được khi bạn phá mã của tôi! Xem thử nghiệm đơn vị tuyệt vời như thế nào!" Trong trường hợp đó, làm cho nó có thể nhạo báng có thể có giá trị ở đây. (Mặc dù, tôi thích nó hơn nếu đối tượng có thể bị chế giễu mà không xác định giao diện)
Winston Ewert

Các bài kiểm tra đơn vị có buộc tôi tạo (hoặc sử dụng) một khung giả để tôi không đạt các thủ tục được lưu trữ trực tiếp không? Đó là lý do chính khiến tôi có xu hướng không thêm các bài kiểm tra - mỗi chúng tôi đều có một bản sao cục bộ của cơ sở dữ liệu sản xuất mà chúng tôi kiểm tra và viết mã chống lại.
Wayne Molina

3
@Anthony Và việc chế giễu luôn biện minh cho chi phí phức tạp thêm mà nó đòi hỏi phải không? Mocking là một công cụ tốt nhưng tính hữu dụng của nó cũng phải được cân nhắc với chi phí và đôi khi quy mô bị nghiêng theo cách khác bởi sự vượt quá của sự gián tiếp. Chắc chắn, có những công cụ để giúp tăng thêm độ phức tạp nhưng chúng sẽ không làm cho sự phức tạp biến mất. Dường như có một xu hướng ngày càng tăng trong việc đối xử với thử nghiệm trên truyền hình với tất cả các chi phí. Tôi tin rằng điều này là sai.
Konrad Rudolph

Câu trả lời:


75

Có một cách tốt để đi đến một sự thỏa hiệp giữa thiết kế phần mềm phù hợp và không bao quát kiến ​​trúc giải pháp?

YAGNI.

Tôi có thể hy sinh một chút thiết kế tốt

Giả định sai.

và giao diện cơ sở và sau đó là bê tông đơn,

Đó không phải là một "sự hy sinh". Đó thiết kế tốt.


72
Sự hoàn hảo đạt được, không phải khi không còn gì để thêm, mà là khi không còn gì để lấy đi. Antoine De St-Exupery
Newtopian

5
@Newtopian: mọi người có cảm xúc lẫn lộn về điều đó liên quan đến các sản phẩm mới nhất của Apple. :)
Roy Tinker

14
Tôi có một vấn đề rất lớn với loại câu trả lời này. Có phải "X là đúng bởi vì Y nói điều đó và Y được cộng đồng ủng hộ" lý do hợp lệ? Nếu ai đó trong đời thực tuyên bố chống lại YAGNI, bạn sẽ chỉ cho anh ta câu trả lời này như một cuộc tranh luận chứ?
vemv

2
@vemv. Không có ai ở đây đang ca ngợi YAGNI. Tất cả S.Lott nói rằng mâu thuẫn nhận thức của OP giữa 2 mục tiêu xứng đáng là một sai lầm. BTW, bạn có biết bất kỳ nhà phát triển tích cực nào trong thế giới phần mềm ngày nay sẽ chống lại YAGNI không? Nếu bạn đang làm việc trên các dự án thực tế, bạn biết rằng các yêu cầu luôn thay đổi và người dùng và người quản lý thường không biết họ làm gì hoặc không muốn gì cho đến khi bạn đặt nó trước mặt họ. Việc thực hiện cần thiết là SỐ LƯỢNG công việc - tại sao phải mất thời gian, năng lượng và lãng phí tiền bạc (hoặc mạo hiểm công việc của bạn) bằng cách viết mã cố gắng dự đoán tương lai?
Vector

6
Quan điểm của tôi không phải là về YAGNI - đó là về chất lượng câu trả lời. Tôi không nói ai đó đặc biệt đang ca ngợi, đó là một phần lý do của tôi. Xin vui lòng đọc lại.
vemv

74

Trong hầu hết các trường hợp, tránh mã mà bạn sẽ không cần dẫn đến thiết kế tốt hơn . Thiết kế dễ bảo trì nhất và trong tương lai là thiết kế sử dụng số lượng nhỏ nhất của mã đơn giản, được đặt tên tốt, đáp ứng các yêu cầu.

Các thiết kế đơn giản nhất là dễ phát triển nhất. Không có gì giết chết khả năng bảo trì như các lớp trừu tượng vô dụng.


8
Tôi thấy 'tránh mã YAGNI' mơ hồ một cách khó hiểu. Điều đó có nghĩa là mã bạn sẽ không cần hoặc mã tuân thủ nguyên tắc YAGNI . (Cf. 'mã KISS')
sehe

2
@sehe: Hoàn toàn không mơ hồ (trong khi "tuân thủ nguyên tắc YAGNI" là hoàn toàn tự mâu thuẫn) nếu bạn đánh vần nó ra: những gì có thể "Mã bạn không cần thiết" có thể có nghĩa nhưng mã đó bạn sẽ không cần?
Michael Borgwardt

1
Tôi sẽ in câu trả lời này bằng một phông chữ lớn và treo nó ở nơi tất cả các phi hành gia kiến ​​trúc có thể đọc nó. +1!
kirk.burleson

1
+1. Tôi vẫn có thể nhớ dự án công ty đầu tiên của mình, tôi đã được hỗ trợ và thêm một số tính năng mới. Điều đầu tiên tôi làm là loại bỏ 32.000 dòng mã vô dụng khỏi chương trình 40.000 dòng mà không mất bất kỳ chức năng nào . Các lập trình viên ban đầu đã bị sa thải ngay sau đó.
EJ Brennan

4
@vemv: đọc "vô dụng" là "hiện không được sử dụng, ngoại trừ tầm thường", tức là một trường hợp của YAGNI. Và "overengineered" là khá cụ thể hơn một chút so với "xấu". Cụ thể nó có nghĩa là "sự phức tạp gây ra bởi các khái niệm lý thuyết hoặc tưởng tượng các yêu cầu có thể, thay vì cụ thể, các yêu cầu hiện tại".
Michael Borgwardt

62

YAGNI và RẮN (hoặc bất kỳ phương pháp thiết kế nào khác) không loại trừ lẫn nhau. Tuy nhiên, chúng là đối lập gần cực. Bạn không cần phải tuân thủ 100% với một trong hai, nhưng sẽ có một số cho và nhận; bạn càng nhìn vào một mô hình trừu tượng hóa cao được sử dụng bởi một lớp ở một nơi và nói YAGNI và đơn giản hóa nó, thiết kế càng trở nên RẮN. Điều ngược lại cũng có thể đúng; nhiều lần trong quá trình phát triển, một thiết kế được thực hiện R "RÀNG" trên đức tin; bạn không thấy bạn sẽ cần nó như thế nào, nhưng bạn chỉ có linh cảm. Điều này có thể đúng (và càng nhiều khả năng là sự thật bạn càng có nhiều kinh nghiệm), nhưng nó cũng có thể khiến bạn mắc nợ kỹ thuật nhiều như cách tiếp cận "làm điều đó nhẹ nhàng"; thay vì một codebase "mã spaghetti" DIL, bạn có thể kết thúc bằng "mã lasagna", có quá nhiều lớp chỉ cần thêm một phương thức hoặc trường dữ liệu mới sẽ trở thành một quá trình dài hàng ngày để lội qua các proxy dịch vụ và các phụ thuộc được kết hợp lỏng lẻo chỉ với một lần thực hiện. Hoặc bạn có thể kết thúc với "mã ravioli", xuất hiện trong các đoạn nhỏ có kích thước khớp cắn di chuyển lên, xuống, trái hoặc phải trong kiến ​​trúc sẽ đưa bạn qua 50 phương thức với 3 dòng mỗi phương thức.

Tôi đã nói điều đó trong các câu trả lời khác, nhưng đây là: Trên đường chuyền đầu tiên, hãy làm cho nó hoạt động. Trên đường chuyền thứ hai, làm cho nó thanh lịch. Trên đường chuyền thứ ba, làm cho nó RẮN.

Phá vỡ nó:

Khi bạn lần đầu tiên viết một dòng mã, nó chỉ đơn giản là phải làm việc. Tại thời điểm này, đối với tất cả những gì bạn biết, đó là một lần. Vì vậy, bạn không nhận được bất kỳ điểm phong cách nào để xây dựng kiến ​​trúc "tháp ngà" để thêm 2 và 2. Làm những gì bạn phải làm và cho rằng bạn sẽ không bao giờ gặp lại nó.

Lần tiếp theo con trỏ của bạn đi theo dòng mã đó, bây giờ bạn đã từ chối giả thuyết của bạn từ khi bạn viết nó lần đầu tiên. Bạn đang xem lại mã đó, có khả năng mở rộng nó hoặc sử dụng nó ở nơi khác, vì vậy nó không phải là một lần. Bây giờ, nên thực hiện một số nguyên tắc cơ bản như DRY (không lặp lại chính mình) và các quy tắc đơn giản khác để thiết kế mã; trích xuất các phương thức và / hoặc tạo các vòng lặp cho mã lặp lại, trích xuất các biến cho các nghĩa đen hoặc biểu thức phổ biến, có thể thêm một số nhận xét, nhưng nói chung, mã của bạn nên tự ghi lại. Bây giờ, mã của bạn được tổ chức tốt, nếu vẫn có thể được liên kết chặt chẽ và bất kỳ ai khác nhìn vào nó cũng có thể dễ dàng tìm hiểu những gì bạn đang làm bằng cách đọc mã, thay vì theo dõi từng dòng một.

Lần thứ ba con trỏ của bạn nhập mã đó, nó có thể là một vấn đề lớn; bạn đang mở rộng nó một lần nữa hoặc nó trở nên hữu ích ở ít nhất ba nơi khác nhau trong cơ sở mã. Tại thời điểm này, đó là một chìa khóa, nếu không phải là yếu tố cốt lõi của hệ thống của bạn và nên được kiến ​​trúc như vậy. Tại thời điểm này, bạn thường có kiến ​​thức về cách sử dụng cho đến nay, điều này sẽ cho phép bạn đưa ra quyết định thiết kế tốt về cách kiến ​​trúc sư thiết kế để hợp lý hóa các cách sử dụng đó và bất kỳ cách sử dụng mới nào. Bây giờ các quy tắc RẮN nên nhập phương trình; trích xuất các lớp chứa mã với các mục đích cụ thể, xác định các giao diện chung cho bất kỳ lớp nào có mục đích hoặc chức năng tương tự, thiết lập các phụ thuộc được ghép lỏng lẻo giữa các lớp và thiết kế các phụ thuộc để bạn có thể dễ dàng thêm, xóa hoặc trao đổi chúng.

Từ thời điểm này, nếu bạn cần tiếp tục mở rộng, thực hiện lại hoặc sử dụng lại mã này, tất cả đều được đóng gói và trừu tượng hóa theo định dạng "hộp đen" mà tất cả chúng ta đều biết và yêu thích; cắm nó vào bất cứ nơi nào bạn cần hoặc thêm một biến thể mới vào chủ đề như là một triển khai mới của giao diện mà không phải thay đổi cách sử dụng giao diện đã nói.


Tôi thứ hai tuyệt vời.
Filip Dupanović

2
Đúng. Khi tôi sử dụng để thiết kế để tái sử dụng / mở rộng trả trước, tôi thấy rằng khi tôi muốn sử dụng lại hoặc mở rộng nó, nó sẽ theo một cách khác so với tôi dự đoán. Dự đoán là khó khăn, đặc biệt là liên quan đến tương lai. Vì vậy, tôi ủng hộ quy tắc 3 lần đình công của bạn - vào thời điểm đó, bạn có một ý tưởng hợp lý về cách nó sẽ được tái sử dụng / gia hạn. NB: một ngoại lệ là nếu bạn đã biết nó sẽ như thế nào (ví dụ từ các dự án trước, kiến ​​thức tên miền hoặc đã được chỉ định).
13ren

Trên đường chuyền thứ tư, làm cho nó TUYỆT VỜI.
Richard Neil Ilagan

@KeithS: Có vẻ như John Carmack đã làm một điều tương tự: "mã nguồn của Quake II ... hợp nhất Quake 1, Quake World và QuakeGL thành một kiến ​​trúc mã đẹp." fabiensanglard.net/quake2/index.php
13ren

+1 Tôi nghĩ tôi sẽ đặt tên cho quy tắc của bạn: "Tái cấu trúc thực tế" :)
Songo

31

Thay vì một trong hai, tôi thích WTSTWCDTUAWCROT?

(Điều đơn giản nhất chúng ta có thể làm là hữu ích và chúng ta có thể phát hành vào thứ năm?)

Từ viết tắt đơn giản nằm trong danh sách những việc cần làm của tôi, nhưng chúng không phải là ưu tiên.


8
Từ viết tắt đó vi phạm nguyên tắc YAGNI :)
riwalk

2
Tôi đã sử dụng chính xác các chữ cái tôi cần - không hơn không kém. Theo cách đó, tôi giống như Mozart. Vâng. Tôi là Mozart của các từ viết tắt.
Mike Sherrill 'Nhớ lại mèo'

4
Tôi chưa bao giờ biết Mozart là khủng khiếp khi tạo ra các từ viết tắt. Tôi học được điều gì đó mới mỗi ngày trên một trang web SE. : P
Cameron MacFarland

3
@Mike Sherrill 'CatRecall': Có lẽ người ta nên mở rộng điều đó thành WTSTWCDTUWCROTAWBOF = "Điều đơn giản nhất chúng ta có thể làm là hữu ích, chúng ta có thể phát hành vào thứ Năm và sẽ không nghỉ vào thứ Sáu?" ;-)
Giorgio

1
@ Stargazer712 không :) nó vi phạm Pola.
v.oddou

25

YAGNI và thiết kế tốt không xung đột. YAGNI là về (không) hỗ trợ các nhu cầu trong tương lai. Thiết kế tốt là làm cho minh bạch những gì phần mềm của bạn làm ngay bây giờ và làm thế nào nó làm điều này.

Sẽ giới thiệu một nhà máy làm cho mã hiện tại của bạn đơn giản hơn? Nếu không, đừng thêm nó. Nếu có, ví dụ như khi bạn thêm các bài kiểm tra (mà bạn nên làm!), Hãy thêm nó.

YAGNI là về việc không thêm sự phức tạp để hỗ trợ các chức năng trong tương lai.
Thiết kế tốt là về loại bỏ sự phức tạp trong khi vẫn hỗ trợ tất cả các chức năng hiện tại.


15

Họ không xung đột, mục tiêu của bạn là sai.

Bạn đang cố gắng để đạt được điều gì?

Bạn muốn viết phần mềm chất lượng và để làm điều đó bạn muốn giữ cho cơ sở mã của bạn nhỏ và không có vấn đề.

Bây giờ chúng tôi đạt đến một cuộc xung đột, làm thế nào để chúng tôi bao gồm tất cả các trường hợp nếu chúng tôi không viết các trường hợp chúng tôi sẽ không sử dụng?

Đây là vấn đề của bạn.

nhập mô tả hình ảnh ở đây (bất cứ ai quan tâm, đây được gọi là những đám mây bốc hơi )

Vì vậy, những gì lái xe này?

  1. Bạn không biết những gì bạn sẽ không cần
  2. Bạn không muốn lãng phí thời gian và làm phồng mã của mình

Mà một trong những chúng ta có thể giải quyết? Chà, có vẻ như không muốn lãng phí thời gian và mã phình là một mục tiêu tuyệt vời, và có ý nghĩa. Cái đầu tiên thì sao? Chúng ta có thể tìm ra những gì chúng ta sẽ cần phải viết mã?

Tôi đang làm việc trong một dự án tại nơi làm việc và muốn từ từ giới thiệu các tiêu chuẩn mã tốt cho đồng nghiệp của mình (hiện tại không có gì và mọi thứ chỉ là bị hack với nhau mà không có vần điệu hoặc lý do) [...] Tôi đã lùi lại một bước và nghĩ rằng nó vi phạm YAGNI vì tôi gần như biết chắc chắn rằng chúng tôi không yêu cầu phải mở rộng một số các lớp này.

Chúng ta hãy diễn đạt lại tất cả những điều đó

  • Không có tiêu chuẩn mã
  • Không có kế hoạch dự án đang diễn ra
  • Những chàng cao bồi ở khắp mọi nơi đang làm điều chết tiệt của riêng họ (và bạn đang cố gắng chơi cảnh sát trưởng ở miền tây hoang dã hoang dã), yee haw.

Có một cách tốt để đi đến một sự thỏa hiệp giữa thiết kế phần mềm phù hợp và không bao quát kiến ​​trúc giải pháp?

Bạn không cần một sự thỏa hiệp, bạn cần một người quản lý nhóm có năng lực và có tầm nhìn về toàn bộ dự án. Bạn cần một người có thể lên kế hoạch cho những gì bạn sẽ cần, thay vì mỗi bạn ném vào những thứ bạn sẽ không cần bởi vì bạn không chắc chắn về tương lai bởi vì ... tại sao? Tôi sẽ cho bạn biết lý do tại sao, bởi vì không ai có kế hoạch chết tiệt giữa tất cả các bạn. Bạn đang cố gắng đưa ra các tiêu chuẩn mã để khắc phục một vấn đề hoàn toàn riêng biệt. Vấn đề CHÍNH của bạn cần giải quyết là một lộ trình và dự án rõ ràng. Khi bạn đã có điều đó, bạn có thể nói "các tiêu chuẩn mã giúp chúng tôi đạt được mục tiêu này hiệu quả hơn với tư cách là một nhóm", đó là sự thật tuyệt đối nhưng nằm ngoài phạm vi của câu hỏi này.

Nhận một người quản lý dự án / nhóm người có thể làm những điều này. Nếu bạn có một bản đồ, bạn cần yêu cầu họ cho bản đồ và giải thích vấn đề YAGNI mà không có bản đồ đang trình bày. Nếu họ không đủ năng lực, hãy tự viết kế hoạch và nói "đây là báo cáo của tôi cho bạn về những điều chúng tôi cần, vui lòng xem lại và cho chúng tôi biết quyết định của bạn."


Đáng buồn thay, chúng ta không có một cái. Trình quản lý phát triển quan tâm nhiều hơn đến việc hoàn thành công việc nhanh hơn tiêu chuẩn / chất lượng hoặc sẽ khiến các thực tiễn tiêu chuẩn như sử dụng DataSets thô ở mọi nơi được trả lại trực tiếp từ các lớp, mã hóa kiểu VB6 và mọi thứ ở phía sau với mã trùng lặp được sao chép và dán kết thúc.
Wayne Molina

Điều đó tốt thôi, nó sẽ chậm hơn một chút, nhưng bạn cần nói ra những lo lắng của họ sau đó. Giải thích rằng vấn đề về mã YAGNI / Vô dụng này đang lãng phí thời gian, đề xuất lộ trình, đưa cho anh ấy một lộ trình, giải thích nó sẽ giúp công việc nhanh hơn. Khi anh ta mua vào, gặp phải những vấn đề mà các tiêu chuẩn kém phải đối với tốc độ phát triển, hãy đề xuất những vấn đề tốt hơn. Họ muốn dự án được thực hiện, họ chỉ không biết cách quản lý mọi thứ, bạn sẽ cần phải thay thế anh ta hoặc sinh con anh ta ... hoặc bỏ việc.
Ẩn danh

Erm ..... Tôi nghĩ mục tiêu lái xe sẽ là 'bạn muốn có một sản phẩm tốt, có giá trị'?
sehe

@sehe đó không phải là quyết định của anh ấy: p.
Ẩn danh

3
Câu trả lời chính xác. Anh ta đang đối mặt với một trận chiến khó khăn. Hoàn thành nó sẽ khó khăn nếu không có quản lý mua. Và bạn đúng rằng câu hỏi đó là quá sớm và không giải quyết được vấn đề thực sự.
Seth Spearman

10

Cho phép mã của bạn được mở rộng bằng các bài kiểm tra đơn vị sẽ không bao giờ được đề cập trong YAGNI, bởi vì bạn sẽ cần nó. Tuy nhiên, tôi không tin rằng thiết kế của bạn thay đổi thành giao diện triển khai đơn lẻ thực sự làm tăng khả năng kiểm tra mã bởi vì CustomerFactory đã kế thừa từ một giao diện và dù sao cũng có thể đổi chỗ cho MockCustomerFactory.


1
+1 cho nhận xét hữu ích về vấn đề thực tế thay vì chỉ về trận chiến vũ trụ và / hoặc tình yêu giữa Thiết kế tốt và YAGNI.
psr

10

Câu hỏi trình bày một vấn đề nan giải giả. Áp dụng đúng nguyên tắc YAGNI không phải là một điều không liên quan. Đó là một khía cạnh của thiết kế tốt. Mỗi nguyên tắc RẮN là các khía cạnh của thiết kế tốt. Bạn không thể luôn áp dụng đầy đủ mọi nguyên tắc trong bất kỳ ngành học nào. Các vấn đề trong thế giới thực đặt rất nhiều lực vào mã của bạn và một số trong số đó đẩy theo các hướng đối nghịch. Nguyên tắc thiết kế phải tính đến tất cả những thứ đó, nhưng không có nguyên tắc nào có thể phù hợp với mọi tình huống.

Bây giờ, chúng ta hãy xem xét từng nguyên tắc với sự hiểu rằng trong khi đôi khi chúng có thể kéo theo các hướng khác nhau, thì chúng hoàn toàn không phải là xung đột.

YAGNI được hình thành để giúp các nhà phát triển tránh một loại công việc đặc biệt: đó là từ việc xây dựng điều sai. Nó thực hiện điều này bằng cách hướng dẫn chúng tôi tránh đưa ra quyết định sai lầm quá sớm dựa trên các giả định hoặc dự đoán về những gì chúng tôi nghĩ sẽ thay đổi hoặc cần thiết trong tương lai. Kinh nghiệm tập thể cho chúng ta biết rằng khi chúng ta làm điều này, chúng ta thường sai. Ví dụ: YAGNI sẽ bảo bạn không tạo giao diện cho mục đích tái sử dụng , trừ khi bạn biết ngay bây giờ rằng bạn cần nhiều người triển khai. Tương tự, YAGNI sẽ nói không tạo "Trình quản lý màn hình" để quản lý biểu mẫu đơn trong một ứng dụng trừ khi bạn biết ngay rằng bạn sẽ có nhiều hơn một màn hình.

Trái với những gì nhiều người nghĩ, RẮN không phải là về khả năng sử dụng lại, tính tổng quát hay thậm chí là trừu tượng. RẮN nhằm giúp bạn viết mã được chuẩn bị để thay đổi , mà không nói bất cứ điều gì về sự thay đổi cụ thể đó có thể là gì. Năm nguyên tắc của RẮN tạo ra một chiến lược xây dựng mã linh hoạt mà không quá chung chung và đơn giản mà không ngây thơ. Áp dụng đúng mã RẮN tạo ra các lớp nhỏ, tập trung với các vai trò và ranh giới được xác định rõ. Kết quả thực tế là đối với bất kỳ yêu cầu cần thiết nào thay đổi, một số lượng tối thiểu các lớp cần phải được chạm vào. Và tương tự, đối với bất kỳ thay đổi mã nào, có một lượng "gợn" tối thiểu thông qua các lớp khác.

Nhìn vào tình huống ví dụ bạn có, hãy xem YAGNI và RẮN có thể nói gì. Bạn đang xem xét một giao diện kho lưu trữ phổ biến do thực tế là tất cả các kho lưu trữ trông giống nhau từ bên ngoài. Nhưng giá trị của một giao diện chung, chung là khả năng sử dụng bất kỳ trình triển khai nào mà không cần biết cụ thể là giao diện nào. Trừ khi có một nơi nào đó trong ứng dụng của bạn, nơi điều này sẽ cần thiết hoặc hữu ích, YAGNI nói đừng làm điều đó.

Có 5 nguyên tắc RẮN để xem xét. S là Trách nhiệm duy nhất. Điều này không nói gì về giao diện, nhưng nó có thể nói điều gì đó về các lớp cụ thể của bạn. Có thể lập luận rằng việc tự xử lý truy cập dữ liệu có thể là trách nhiệm của một hoặc nhiều lớp khác, trong khi trách nhiệm của kho lưu trữ là dịch từ ngữ cảnh ngầm (CustomerRep repository là một kho lưu trữ ngầm cho các thực thể Khách hàng) thành các cuộc gọi rõ ràng đến API truy cập dữ liệu tổng quát chỉ định loại thực thể Khách hàng.

O là Đóng-Đóng. Điều này chủ yếu là về thừa kế. Nó sẽ được áp dụng nếu bạn đang cố gắng lấy các kho lưu trữ của mình từ một cơ sở chung triển khai chức năng chung hoặc nếu bạn dự kiến ​​sẽ lấy thêm từ các kho khác nhau. Nhưng bạn thì không, vì vậy nó không.

L là Liskov Thay thế. Điều này áp dụng nếu bạn có ý định sử dụng các kho lưu trữ thông qua giao diện kho lưu trữ chung. Nó đặt các hạn chế trên giao diện và triển khai để đảm bảo tính nhất quán và tránh xử lý đặc biệt cho các yếu tố khác nhau. Lý do cho điều này là việc xử lý đặc biệt như vậy làm suy yếu mục đích của một giao diện. Có thể hữu ích khi xem xét nguyên tắc này, bởi vì nó có thể cảnh báo bạn khỏi việc sử dụng giao diện kho lưu trữ chung. Điều này trùng với hướng dẫn của YAGNI.

Tôi là phân chia giao diện. Điều này có thể áp dụng nếu bạn bắt đầu thêm các hoạt động truy vấn khác nhau vào kho lưu trữ của mình. Phân tách giao diện áp dụng trong đó bạn có thể chia các thành viên của một lớp thành hai tập con, trong đó một tập hợp sẽ được sử dụng bởi một số người tiêu dùng và người khác, nhưng không người tiêu dùng nào có thể sử dụng cả hai tập hợp con. Hướng dẫn là tạo hai giao diện riêng biệt, thay vì một giao diện chung. Trong trường hợp của bạn, không có khả năng tìm nạp và lưu các cá thể riêng lẻ sẽ được sử dụng bởi cùng một mã sẽ thực hiện truy vấn chung, do đó, có thể hữu ích khi tách chúng thành hai giao diện.

D là tiêm phụ thuộc. Ở đây chúng ta quay trở lại cùng một điểm với S. Nếu bạn tách mức tiêu thụ API truy cập dữ liệu của mình thành một đối tượng riêng biệt, nguyên tắc này nói rằng thay vì chỉ tạo một thể hiện của đối tượng đó, bạn nên chuyển nó vào khi bạn tạo một kho lưu trữ. Điều này giúp kiểm soát tuổi thọ của thành phần truy cập dữ liệu dễ dàng hơn, mở ra khả năng chia sẻ các tham chiếu đến nó giữa các kho lưu trữ của bạn mà không cần phải đi theo con đường biến nó thành một singleton.

Điều quan trọng cần lưu ý là hầu hết các nguyên tắc RẮN không nhất thiết phải áp dụng ở giai đoạn phát triển ứng dụng cụ thể này. Ví dụ, việc bạn có nên thoát ra khỏi truy cập dữ liệu hay không tùy thuộc vào mức độ phức tạp của nó và liệu bạn có muốn kiểm tra logic kho lưu trữ của mình mà không cần nhấn cơ sở dữ liệu hay không. Nghe có vẻ như điều này là không thể (thật không may, theo ý kiến ​​của tôi), vì vậy có lẽ không cần thiết.

Vì vậy, sau tất cả những cân nhắc đó, chúng tôi thấy rằng YAGNI và RẮN thực sự cung cấp một lời khuyên chung, có liên quan ngay lập tức: Có lẽ không cần thiết phải tạo giao diện kho lưu trữ chung chung.

Tất cả những suy nghĩ cẩn thận này là vô cùng hữu ích như một bài tập học tập. Nó tốn thời gian khi bạn học, nhưng theo thời gian, bạn phát triển trực giác và trở nên rất nhanh. Bạn sẽ biết điều đúng cần làm, nhưng không cần phải nghĩ tất cả những từ này trừ khi có ai đó yêu cầu bạn giải thích lý do.


Tôi nghĩ rằng hầu hết các cuộc thảo luận trong trang này đều bỏ qua 2 ứng cử viên lớn là "khớp nối thấp" và "sự gắn kết cao" của GRASPrinciples. Các quyết định thiết kế đắt tiền bắt nguồn từ nguyên tắc "khớp nối thấp". Giống như khi nào "kích hoạt" SRP + ISP + DIP vì lợi ích của khớp nối thấp. Ví dụ: một lớp -> 3 lớp trong mẫu MVC. Hoặc thậm chí đắt hơn: chia thành các mô-đun / cụm. Điều đó rất tốn kém vì ý nghĩa xây dựng, dự án, makelist, máy chủ xây dựng, bổ sung các tệp phiên bản nguồn ...
v.oddou

6

Bạn dường như tin rằng 'thiết kế tốt' có nghĩa là tuân theo một số quy tắc chính thức và ý thức phải luôn được áp dụng, ngay cả khi vô dụng.

IMO đó là thiết kế tồi. YAGNI là một thành phần của thiết kế tốt, không bao giờ mâu thuẫn với nó.


2

Trong ví dụ của bạn, tôi sẽ nói rằng YAGNI nên thắng thế. Bạn sẽ không mất nhiều chi phí nếu bạn cần thêm giao diện sau này. Nhân tiện, nó có thực sự được thiết kế tốt để có một giao diện theo lớp nếu nó không phục vụ mục tiêu nào không?

Thêm một suy nghĩ, có thể đôi khi điều bạn cần không phải là Thiết kế tốt mà là Thiết kế đầy đủ. Dưới đây là chuỗi bài viết rất thú vị về chủ đề này:


Liên kết đầu tiên và thứ ba của bạn đi đến cùng một nơi.
David Thornley

2

Một số người tranh luận rằng tên giao diện không nên bắt đầu bằng I. Cụ thể một lý do là, bạn thực sự rò rỉ sự phụ thuộc vào việc loại đã cho là một lớp hay một giao diện.

Điều gì cấm bạn CustomerFactorytrở thành một lớp học lúc đầu và sau đó thay đổi nó thành một giao diện, điều đó sẽ được thực hiện bởi DefaultCustormerFactoryhoặc UberMegaHappyCustomerPowerFactory3000? Điều duy nhất bạn cần phải thay đổi là địa điểm, nơi triển khai thực hiện ngay lập tức. Và nếu bạn có một thiết kế kém tốt hơn, thì đây là một số ít nơi nhất.

Tái cấu trúc là một phần của sự phát triển. Tốt hơn là có ít mã, dễ tái cấu trúc, hơn là có một giao diện và một lớp được khai báo cho mỗi lớp duy nhất, buộc bạn phải thay đổi mọi tên phương thức ở ít nhất hai vị trí cùng một lúc.

Điểm thực sự trong việc sử dụng các giao diện là đạt được tính mô đun, có thể là trụ cột quan trọng nhất của thiết kế tốt. Tuy nhiên, lưu ý rằng một mô-đun không chỉ được xác định bằng cách tách rời khỏi thế giới bên ngoài (mặc dù đó là cách chúng ta nhìn nhận nó từ góc nhìn bên ngoài), mà còn bằng cách hoạt động bên trong của nó.
Điều tôi muốn nói là, việc tách rời mọi thứ, vốn dĩ thuộc về nhau không có ý nghĩa gì nhiều. Theo một cách nào đó, nó giống như có một tủ với một kệ riêng cho mỗi cốc.

Tầm quan trọng nằm ở việc đưa ra một vấn đề lớn, phức tạp thành các bài toán con nhỏ hơn, đơn giản hơn. Và bạn phải dừng lại ở điểm, nơi chúng trở nên đủ đơn giản mà không cần phân chia thêm, nếu không thì thực tế chúng sẽ trở nên phức tạp hơn. Đây có thể được coi là một hệ quả của YAGNI. Và nó chắc chắn có nghĩa là thiết kế tốt.

Mục tiêu không phải là bằng cách nào đó giải quyết một vấn đề cục bộ với một kho lưu trữ duy nhất và một nhà máy duy nhất. Mục tiêu là, quyết định này không có hiệu lực đối với phần còn lại của ứng dụng của bạn. Đó là những gì mô-đun là về.
Bạn muốn đồng nghiệp của mình nhìn vào mô-đun của bạn, nhìn thấy một mặt tiền với một số cuộc gọi tự giải thích và cảm thấy tự tin, rằng họ có thể sử dụng chúng, mà không phải lo lắng về tất cả các tình huống bên trong phức tạp.


0

Bạn đang tạo ra một interfacetriển khai dự đoán trong tương lai. Bạn cũng có thể có một I<class>lớp cho mỗi lớp trong cơ sở mã của bạn. Đừng.

Chỉ cần sử dụng lớp bê tông duy nhất theo YAGNI. Nếu bạn thấy bạn cần phải có một đối tượng "giả" cho mục đích thử nghiệm, hãy biến lớp ban đầu thành một lớp trừu tượng với hai triển khai, một với lớp cụ thể ban đầu và lớp kia với triển khai giả.

Rõ ràng là bạn sẽ phải cập nhật tất cả các tức thời của lớp gốc để khởi tạo lớp cụ thể mới. Bạn có thể khắc phục điều đó bằng cách sử dụng hàm tạo tĩnh lên phía trước.

YAGNI nói không viết mã trước khi cần viết.

Thiết kế tốt nói sử dụng trừu tượng.

Bạn có thể có cả hai. Một lớp học là một sự trừu tượng.


0

Tại sao các giao diện đánh dấu? Tôi nhận ra rằng nó không làm gì hơn là "gắn thẻ". Với một "thẻ" khác nhau cho mỗi loại nhà máy, vấn đề là gì?

Mục đích của giao diện là cung cấp cho một lớp "hành vi như" hành vi, để cung cấp cho họ "khả năng lưu trữ" để nói. Vì vậy, nếu tất cả các loại kho lưu trữ cụ thể của bạn hoạt động giống như cùng một Kho lưu trữ (tất cả chúng đều thực hiện IRep repository) thì tất cả chúng có thể được xử lý theo cùng một cách bởi mã khác - bằng cùng một mã. Tại thời điểm này thiết kế của bạn có thể mở rộng. Thêm nhiều loại kho lưu trữ cụ thể, tất cả được xử lý dưới dạng IRep repository chung - cùng một mã xử lý tất cả các loại cụ thể dưới dạng kho "chung".

Các giao diện là để xử lý mọi thứ dựa trên sự tương đồng. Nhưng giao diện đánh dấu tùy chỉnh a) thêm không có hành vi. và b) buộc bạn phải xử lý tính độc đáo của chúng.

Trong phạm vi bạn thiết kế các giao diện hữu ích, bạn nhận được ưu điểm của OO là không phải viết mã chuyên dụng để xử lý các lớp, loại hoặc giao diện đánh dấu tùy chỉnh có tương quan 1: 1 với các lớp cụ thể. Đó là sự dư thừa vô nghĩa.

Tôi có thể thấy một giao diện đánh dấu, ví dụ, nếu bạn cần một bộ sưu tập được gõ mạnh của nhiều lớp khác nhau. Trong bộ sưu tập, tất cả chúng đều là "ImarkerInterface" nhưng khi bạn kéo chúng ra, bạn phải chuyển chúng thành các loại thích hợp.


0

Bạn có thể, ngay bây giờ, viết ra một ICustomerRep repository mơ hồ hợp lý không? Ví dụ: (thực sự đạt đến đây, có lẽ là một ví dụ tồi tệ) trong quá khứ khách hàng của bạn đã luôn sử dụng PayPal? Hoặc tất cả mọi người trong công ty đang lo lắng về việc kết nối với Alibaba? Nếu vậy, bạn có thể muốn sử dụng thiết kế phức tạp hơn bây giờ và có vẻ viễn thị với các ông chủ của bạn. :-)

Nếu không, chờ đợi. Đoán tại một giao diện trước khi bạn có một hoặc hai triển khai thực tế thường thất bại. Nói cách khác, không khái quát hóa / trừu tượng / sử dụng một mẫu thiết kế lạ mắt cho đến khi bạn có một vài ví dụ để khái quát hóa.

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.