Giải thích nguyên tắc DRY


10

Ngay bây giờ tôi đang vật lộn với khái niệm DRY (Đừng lặp lại chính mình) trong mã hóa của mình. Tôi đang tạo chức năng này trong đó tôi sợ nó trở nên quá phức tạp nhưng tôi đang cố gắng tuân theo nguyên tắc DRY.

createTrajectoryFromPoint(A a,B b,C c,boolean doesSomething,boolean doesSomething2)

Hàm này tôi đã nói có 3 tham số đầu vào, và sau đó hàm sẽ làm một cái gì đó hơi khác với các kết hợp boolean doesSomethingdoesSomething2. Tuy nhiên, vấn đề tôi gặp phải là chức năng này đang phát triển rất phức tạp với mỗi tham số boolean mới được thêm vào.

Vì vậy, câu hỏi của tôi là, tốt hơn là có một loạt các chức năng khác nhau có chung nhiều logic (do đó vi phạm nguyên tắc DRY) hoặc một chức năng hoạt động hơi khác nhau với một số tham số nhưng làm cho nó phức tạp hơn nhiều (nhưng bảo quản DRY)?


3
Logic chung / chung có thể được đưa vào các hàm riêng mà các createTrajectory...hàm công khai khác nhau đều gọi không?
Thất vọngWithFormsDesigner

Có thể là vậy nhưng các hàm riêng đó vẫn cần lấy các tham số boolean đó
Albinoswordfish

2
Tôi nghĩ rằng điều này sẽ / sẽ dễ dàng hơn rất nhiều để trả lời với một số ví dụ cụ thể. Phản ứng ngay lập tức của tôi là sự phân đôi mà bạn miêu tả không hoàn toàn có thật - tức là, đó không phải là hai lựa chọn duy nhất. Bên cạnh đó, tôi sẽ xem xét bất kỳ việc sử dụng booleannhư là một tham số hơi nghi ngờ.
Jerry Coffin


Uh, tại sao bạn không bao gồm các điều kiện vào chức năng riêng của họ?
Giàn khoan

Câu trả lời:


19

các đối số boolean để kích hoạt các đường dẫn mã khác nhau trong một hàm / phương thức duy nhất là một mùi mã khủng khiếp .

Những gì bạn đang làm vi phạm các nguyên tắc Khớp nối lỏng lẻoSự gắn kết caoTrách nhiệm đơn lẻ , điều này quan trọng hơn nhiều so với DRY trước đây.

Điều đó có nghĩa là mọi thứ chỉ nên phụ thuộc vào những thứ khác khi chúng phải ( Khớp nối ) và chúng nên làm một việc và chỉ một việc (rất tốt) ( Sự gắn kết ).

Theo thiếu sót của riêng bạn, điều này được kết hợp quá chặt chẽ (tất cả các cờ boolean là một loại phụ thuộc trạng thái, là một trong những điều tồi tệ nhất!) Và có quá nhiều trách nhiệm cá nhân xen kẽ (quá phức tạp).

Những gì bạn đang làm không phải là tinh thần của DRY. DRY là nhiều hơn về sự lặp lại ( Rviết tắt của REPEAT). Tránh sao chép và dán là hình thức cơ bản nhất của nó. Những gì bạn đang làm không liên quan đến sự lặp lại.

Vấn đề của bạn là sự phân rã mã của bạn không ở mức chính xác. Nếu bạn nghĩ rằng bạn sẽ có mã trùng lặp, thì đó phải là chức năng / phương thức riêng của nó được tham số hóa phù hợp, không sao chép và dán, và các mã khác nên được đặt tên mô tả và ủy quyền cho chức năng / phương thức cốt lõi.


Ok có vẻ như cách tôi viết không tốt lắm (sự nghi ngờ ban đầu của tôi) Tôi không thực sự chắc chắn đây là 'Sprit of DRY' là gì
Albinoswordfish


4

Việc bạn chuyển qua booleans để thực hiện chức năng làm những việc khác nhau là vi phạm Nguyên tắc Trách nhiệm duy nhất. Một chức năng nên làm một điều. Nó nên làm một điều duy nhất, và nó nên làm điều đó tốt.

Có vẻ như bạn cần chia nó thành nhiều hàm nhỏ hơn với các tên mô tả, tách các đường dẫn mã tương ứng với các giá trị của các booleans đó.

Khi bạn làm điều đó, bạn nên tìm mã phổ biến trong các hàm kết quả và đưa mã đó vào (các) hàm riêng của nó. Tùy thuộc vào mức độ phức tạp của điều này, bạn thậm chí có thể tính ra một hoặc hai lớp.

Tất nhiên, điều này giả định rằng bạn đang sử dụng một hệ thống kiểm soát phiên bản và bạn có một bộ kiểm tra tốt, để bạn có thể cấu trúc lại mà không sợ phá vỡ thứ gì đó.


3

Tại sao bạn không tạo một hàm khác chứa tất cả logic trong hàm của mình trước khi bạn quyết định làm một cái gì đó hoặc một cái gì đó2 và sau đó có ba hàm như:

createTrajectoryFromPoint(A a,B b,C c){...}

dosomething(A a, B b, C c){...}

dosomething2(A a, B b, C c){...}

Và bây giờ bằng cách chuyển ba loại tham số giống nhau cho ba hàm khác nhau, bạn sẽ lại lặp lại chính mình, vì vậy bạn nên xác định một cấu trúc hoặc lớp có chứa A, B, C.

Ngoài ra, bạn có thể tạo một lớp chứa các tham số A, B, C và một danh sách các hoạt động sẽ được thực hiện. Thêm các hoạt động (một cái gì đó, một cái gì đó 2) bạn muốn xảy ra với các tham số này (A, B, C) bằng cách đăng ký các hoạt động với đối tượng. Sau đó, có một phương thức để gọi tất cả các hoạt động đã đăng ký trên đối tượng của bạn.

public class MyComplexType
{
    public A a{get;set;}
    public B b{get;set;}
    public C c{get;set;}

    public delegate void Operation(A a, B b, C c);
    public List<Operation> Operations{get;set;}

    public MyComplexType(A a, B b, C c)
    {
        this.a = a;
        this.b = b;
        this.c = c   
        Operations = new List<Operation>();
    }

    public CallMyOperations()
    {
        foreach(var operation in Operations)
        {
            operation(a,b,c);
        }
    }
}

Để tính đến sự kết hợp các giá trị có thể có của booleans dos somebody và dos Something2, bạn sẽ cần 4 hàm, không phải 2, ngoài chức năng createdTrajectionyFromPoint cơ sở. Cách tiếp cận này không mở rộng tốt khi số lượng tùy chọn tăng lên và thậm chí việc đặt tên cho các chức năng trở nên tẻ nhạt.
JGweissman

2

DRY có thể được đưa đi quá xa, tốt nhất là sử dụng nguyên tắc trách nhiệm duy nhất (SRP) kết hợp với DRY. Thêm cờ bool vào một hàm để làm cho nó thực hiện các phiên bản khác nhau của cùng một mã có thể là một dấu hiệu bạn đang làm quá nhiều với một hàm. Trong trường hợp này, tôi khuyên bạn nên tạo một hàm riêng cho từng trường hợp mà cờ của bạn đại diện, sau đó khi bạn viết từng hàm thì sẽ khá rõ ràng nếu có một phần chung có thể được chuyển sang hàm riêng mà không chuyển tất cả các cờ , nếu không có phần mã rõ ràng, thì bạn thực sự không lặp lại chính mình, bạn có một số trường hợp khác nhau nhưng tương tự.


1

Tôi thường trải qua một vài bước với vấn đề này, dừng lại khi không thể tìm ra cách đi xa hơn.

Đầu tiên, làm những gì bạn đã làm. Đi khó với DRY. Nếu bạn không kết thúc với một mớ lông lớn, bạn đã hoàn thành. Nếu, như trong trường hợp của bạn, bạn không có mã trùng lặp nhưng mỗi boolean có giá trị được kiểm tra ở 20 vị trí khác nhau, hãy chuyển sang bước tiếp theo.

Thứ hai, chia mã thành các khối. Các booleans mỗi tham chiếu chỉ một lần (tốt, đôi khi có thể hai lần) để thực hiện trực tiếp đến khối bên phải. Với hai booleans, bạn kết thúc với bốn khối. Mỗi khối gần như giống hệt nhau. DRY đã biến mất Đừng biến mỗi khối thành một phương thức riêng biệt. Điều đó sẽ thanh lịch hơn, nhưng đặt tất cả mã vào một phương thức sẽ giúp dễ dàng hơn, hoặc thậm chí có thể, cho bất kỳ ai đang bảo trì để thấy rằng họ phải thực hiện mỗi thay đổi ở bốn nơi. Với mã được tổ chức tốt và một màn hình cao, sự khác biệt và sai lầm sẽ gần như rõ ràng. Bây giờ bạn có mã duy trì nó sẽ chạy nhanh hơn mớ hỗn độn ban đầu.

Thứ ba, cố gắng lấy các dòng mã trùng lặp từ mỗi khối của bạn và biến chúng thành các phương thức đơn giản, đẹp mắt. Đôi khi bạn không thể làm gì. Đôi khi bạn không thể làm gì nhiều. Nhưng mỗi một chút bạn làm sẽ đưa bạn trở lại DRY và làm cho mã dễ theo dõi hơn một chút và an toàn hơn để duy trì. Lý tưởng nhất là phương pháp ban đầu của bạn có thể không có mã trùng lặp. Tại thời điểm đó, bạn có thể muốn chia nó thành nhiều phương thức mà không có các tham số boolean hoặc bạn có thể không. Sự tiện lợi của mã gọi hiện đang là mối quan tâm chính.

Tôi đã thêm câu trả lời của mình vào số lượng lớn đã có ở đây vì bước thứ hai. Tôi ghét mã trùng lặp, nhưng nếu đó là cách dễ hiểu duy nhất để giải quyết vấn đề, hãy thực hiện theo cách mà bất kỳ ai cũng sẽ biết trong nháy mắt những gì bạn đang làm. Sử dụng nhiều khối và chỉ một phương thức. Làm cho các khối giống hệt nhau nhất có thể trong tên, khoảng cách, sắp xếp, ... tất cả mọi thứ. Sự khác biệt sau đó nên nhảy ra ở người đọc. Nó có thể làm cho nó rõ ràng làm thế nào để viết lại nó theo cách DRY, và nếu không, việc duy trì nó sẽ đơn giản hợp lý.


0

Một cách tiếp cận khác là thay thế các tham số boolean bằng các tham số giao diện, bằng mã để xử lý các giá trị boolean khác nhau được tái cấu trúc thành các cài đặt giao diện. Vì vậy, bạn sẽ có

createTrajectoryFromPoint(A a,B b,C c,IX x,IY y)

nơi bạn có các triển khai IX và IY đại diện cho các giá trị khác nhau cho các booleans. Trong cơ thể của chức năng, bất cứ nơi nào bạn có

if (doesSomething)
{
     ...
}
else
{
     ...
}

bạn thay thế nó bằng một cuộc gọi đến một phương thức được xác định trên IX, với các cài đặt có chứa các khối mã bị bỏ qua.

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.