Có thể chấp nhận sao chép và dán mã dài nhưng đơn giản thay vì gói chúng vào một lớp hoặc hàm?


29

Giả sử tôi có một đoạn mã để kết nối với internet và hiển thị kết quả kết nối như thế này:

HttpRequest* httpRequest=new HttpRequest();
httpRequest->setUrl("(some domain .com)");
httpRequest->setRequestType(HttpRequest::Type::POST);
httpRequest->setRequestData("(something like name=?&age=30&...)");
httpRequest->setResponseCallback([=](HttpClient* client, HttpResponse* response){
    string responseString=response->getResponseDataString();
        if(response->getErrorCode()!=200){
            if(response->getErrorCode()==404){
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setFontColor(255,255,255);
                alert->setPosition(Screen.MIDDLE);
                alert->show("Connection Error","Not Found");
            }else if((some other different cases)){
                (some other alert)
            }else
                Alert* alert=new Alert();
                alert->setFontSize(30);
                alert->setPosition(Screen.MIDDLE);
                alert->setFontColor(255,255,255);
                alert->show("Connection Error","unknown error");
            }
        }else{
            (other handle methods depend on different URL)
        }
}

mã này dài và được sử dụng phổ biến, nhưng mã ở trên không yêu cầu thêm bất kỳ thứ gì như chức năng và lớp tùy chỉnh (cả HTTPRequest và Alert đều được cung cấp theo khung theo mặc định) và mặc dù đoạn mã dài, nhưng nó là Đơn giản và không phức tạp (lâu nay chỉ vì có các gói cài đặt như url, cỡ chữ ...) và phân đoạn mã có ít biến thể giữa các lớp (ví dụ: url, dữ liệu yêu cầu, trường hợp xử lý mã lỗi, xử lý thông thường các trường hợp...)

Câu hỏi của tôi là, có thể chấp nhận sao chép và dán mã dài nhưng đơn giản thay vì gói chúng trong một hàm để giảm sự phụ thuộc của mã không?


89
Hãy tưởng tượng bạn có một lỗi trong mã đó, chẳng hạn như không giải phóng các đối tượng bạn phân bổ. (Khung của bạn có giải phóng các Alertđối tượng không?) Bây giờ hãy tưởng tượng bạn phải tìm mọi phiên bản được sao chép của mã này để sửa lỗi. Bây giờ hãy tưởng tượng không phải bạn là người phải làm điều đó, mà là một kẻ giết người rìu điên rồ, người biết bạn là người đã tạo ra tất cả những bản sao này ngay từ đầu.
Sebastian Redl

8
Và BTW, trộn mạng và hiển thị lỗi ở một nơi đã là một điều không nên, IMHO.
sleske

11
Không bao giờ. Hoàn toàn không thể chấp nhận được. Nếu bạn ở trong dự án của tôi, bạn sẽ không còn tham gia dự án của tôi nữa và bạn sẽ tham gia chương trình đào tạo hoặc PIP.
nhgrif

10
Và cùng với người giám sát nói rằng "Hộp cảnh báo ở giữa màn hình của tôi là một nỗi kinh hoàng tuyệt đối. Tôi đang xem mèo jifs và cửa sổ bật lên đang chặn tầm nhìn của tôi mỗi khi nó xuất hiện. Vui lòng di chuyển nó sang phía trên bên phải . " 3 tuần sau "Bạn đã làm cái quái gì vậy?!
MonkeyZeus

11
Tôi nghĩ mọi người ở đây dường như nghĩ rằng đây là một ý tưởng tồi. Nhưng để xoay chuyển câu hỏi, tại sao bạn KHÔNG đặt mã này vào một lớp hoặc hàm riêng biệt?
Karl Gjertsen

Câu trả lời:


87

Bạn cần xem xét chi phí thay đổi. Điều gì nếu bạn muốn thay đổi cách kết nối được thực hiện? Làm thế nào dễ dàng? Nếu bạn có nhiều mã trùng lặp, thì việc tìm tất cả các vị trí cần thay đổi có thể khá tốn thời gian và dễ bị lỗi.

Bạn cũng cần xem xét sự rõ ràng. Rất có thể, việc phải xem 30 dòng mã sẽ không dễ hiểu như một cuộc gọi đến chức năng "connectToI Internet". Mất bao nhiêu thời gian để cố gắng hiểu mã khi cần thêm chức năng mới?

Có một số trường hợp hiếm hoi mà sự trùng lặp không phải là vấn đề. Ví dụ: nếu bạn đang thực hiện một thử nghiệm và mã sẽ bị vứt đi vào cuối ngày. Nhưng nói chung, chi phí sao chép lớn hơn tiết kiệm thời gian nhỏ khi không phải kéo mã ra thành một chức năng riêng biệt .

Xem thêm https://softwareengineering.stackexchange.com/a/103235/63172


19
... và ai biết được nếu 30 dòng này thực sự giống với những dòng khác mà bạn đã xem trước đó hoặc nếu ai đó chuyển sang một cổng hoặc địa chỉ IP khác trong bản sao của anh ấy / cô ấy vì bất kỳ lý do gì. Giả định ngầm định rằng " bất kỳ bó nào trong số 30 dòng bắt đầu bằng HttpRequestđều giống nhau " là một cách dễ dàng để tạo ra ngụy biện.
null

@null Điểm tuyệt vời. Tôi đã từng làm việc với mã nơi có các kết nối cơ sở dữ liệu được sao chép và dán khắp nơi, nhưng một số có sự khác biệt tinh tế trong thiết lập. Tôi không biết liệu đây có phải là những thay đổi quan trọng, có chủ ý hay chỉ là những khác biệt ngẫu nhiên
user949300

54

Không.

Trong thực tế, ngay cả mã "đơn giản" của bạn cũng nên được chia thành các phần nhỏ hơn. Ít nhất hai.

Một để thực hiện kết nối và xử lý 200 phản hồi bình thường. Ví dụ, nếu bạn thay đổi từ POST thành PUT trong một số trường hợp thì sao? Điều gì sẽ xảy ra nếu bạn đang tạo ra hàng trăm kết nối này và cần một số luồng đa luồng hoặc kết nối? Có mã ở một nơi duy nhất, với một đối số cho phương thức, sẽ giúp việc này dễ dàng hơn nhiều

Tương tự, khác để xử lý lỗi. Ví dụ: nếu bạn thay đổi màu sắc hoặc kích thước phông chữ của cảnh báo. Hoặc bạn đang gặp vấn đề với các kết nối không liên tục và muốn ghi lại các lỗi.



Bạn cũng có thể trích dẫn SRP: có một khối mã chỉ có một mục đích giúp cho việc hiểu và duy trì dễ dàng hơn nhiều ..
Roland Tepp

Đây là một trường hợp trong đó DRY và SRP thực sự thẳng hàng. Đôi khi họ không.
dùng949300

18

sao chép và dán ...

Không.

Đối với tôi, đối số quyết định là điều này:

... nó thường được sử dụng ...

Nếu bạn sử dụng một đoạn mã ở nhiều nơi sau đó, khi nó thay đổi, bạn phải thay đổi nó ở nhiều nơi hoặc bạn bắt đầu có sự không nhất quán - "những điều kỳ lạ" bắt đầu xảy ra (ví dụ như bạn giới thiệu Bugs).

nó đơn giản và không phức tạp ...

Và do đó, nên dễ dàng tái cấu trúc thành một hàm.

... có các gói cài đặt như url, cỡ chữ ...

Và những gì người dùng thích thay đổi? Phông chữ, cỡ chữ, màu sắc, v.v.

Hiện nay; ở bao nhiêu nơi bạn sẽ phải thay đổi cùng một đoạn mã để lấy lại tất cả chúng cùng màu / phông chữ / kích thước? (Đề nghị trả lời: chỉ một ).

... phân đoạn mã có ít biến thể giữa các lớp (ví dụ: url, dữ liệu yêu cầu, trường hợp xử lý mã lỗi, trường hợp xử lý thông thường ...)

Biến thể => tham số hàm (s).


"Và những gì người dùng thích thay đổi? Phông chữ, kích thước phông chữ, màu sắc, v.v." điều này rất hợp lý. Bạn có thực sự muốn thay đổi điều đó trong hàng chục địa điểm?
Dan

8

Điều này thực sự không có gì để làm với sao chép và dán. Nếu bạn lấy mã từ nơi khác, tại thứ hai bạn lấy mã đó là bạn mã và bạn có trách nhiệm, vì vậy cho dù đó là sao chép hay viết hoàn toàn bởi bản thân không tạo sự khác biệt.

Trong cảnh báo của bạn, bạn đưa ra một số quyết định thiết kế. Nhiều khả năng các quyết định thiết kế tương tự nên được đưa ra cho tất cả các cảnh báo. Vì vậy, có khả năng là bạn nên có một phương thức ở đâu đó "ShowAlertInAStyleSuitableForMyApplication" hoặc có thể ngắn hơn một chút, và nên gọi nó.

Bạn sẽ có rất nhiều yêu cầu http với xử lý lỗi tương tự. Bạn có thể không nên lặp lại việc xử lý lỗi lặp đi lặp lại mà trích xuất xử lý lỗi phổ biến. Đặc biệt là nếu việc xử lý lỗi của bạn trở nên phức tạp hơn một chút (về lỗi hết thời gian, 401, v.v.).


6

Sao chép là OK trong một số trường hợp. Nhưng không phải trong cái này. Phương pháp đó quá phức tạp. Có giới hạn thấp hơn, khi sao chép dễ dàng hơn "bao thanh toán" một phương thức.

Ví dụ:

def add(a, b)
    return a + b
end

là ngu ngốc, chỉ cần làm a + b.

Nhưng khi bạn trở nên phức tạp hơn một chút, thì bạn thường vượt qua ranh giới.

foo.a + foo.b

nên trở thành

foo.total
def foo
    ...
    def total
        return self.a + self.b
    end
end

Trong trường hợp của bạn, tôi thấy bốn "phương pháp". Có lẽ trong các lớp học khác nhau. Một để thực hiện yêu cầu, một để nhận phản hồi, một để hiển thị lỗi và một số loại cuộc gọi trở lại được gọi sau khi phản hồi trở lại để xử lý phản hồi. Cá nhân tôi có thể sẽ thêm một "trình bao bọc" các loại trên đầu trang để làm cho các cuộc gọi dễ dàng hơn.

Cuối cùng, để thực hiện một yêu cầu web, tôi muốn một cuộc gọi trông giống như:

Web.Post(URI, Params, ResponseHandler);

Dòng đó là những gì tôi sẽ có tất cả trên mã của tôi. Sau đó, khi tôi cần thay đổi "cách tôi lấy đồ", tôi có thể nhanh chóng làm như vậy, với ít nỗ lực hơn.

Điều này cũng giữ mã DRY và giúp với SRP .


0

Trong một dự án có kích thước / độ phức tạp bất kỳ, tôi muốn có thể tìm mã khi tôi cần nó cho các mục đích sau:

  1. Sửa nó khi nó bị hỏng
  2. Thay đổi chức năng
  3. Tái sử dụng nó.

Sẽ không đáng yêu, để tham gia một dự án đang tiến hành hoặc tiếp tục làm việc trong một dự án trong vài năm và khi một yêu cầu mới "kết nối với internet và hiển thị kết quả kết nối" ở một vị trí dễ tìm thấy do có một vị trí dễ tìm thiết kế tốt thay vì dựa vào việc tìm kiếm trong toàn bộ mã cho một httprequest? Có thể dễ dàng tìm thấy hơn với Google.

Đừng lo lắng, tôi là người mới và tôi sẽ cấu trúc lại khối mã này bởi vì tôi rất buồn khi tham gia nhóm không biết gì về cơ sở mã khủng khiếp này hoặc bây giờ tôi chịu nhiều áp lực như phần còn lại của bạn và sẽ chỉ sao chép và dán nó. Ít nhất nó sẽ giữ ông chủ khỏi lưng tôi. Sau đó, khi dự án thực sự được xác định là thảm họa, tôi sẽ là người đầu tiên đề nghị chúng tôi viết lại nó bằng cách sao chép và dán phiên bản trong khuôn khổ mới nhất và lớn nhất mà không ai trong chúng tôi hiểu được.


0

Một lớp và / hoặc một chức năng là tốt hơn, ít nhất là theo ý kiến ​​của tôi. Lần đầu tiên, nó làm cho tệp nhỏ hơn, mức tăng rất nặng nếu bạn xử lý các ứng dụng web hoặc ứng dụng cho các thiết bị có ít bộ nhớ (IoT, điện thoại cũ, v.v.)

Và rõ ràng điểm tốt nhất là nếu bạn có điều gì đó cần thay đổi do các giao thức mới, v.v. bạn chỉ cần thay đổi nội dung của hàm và không phải vô số lần bạn đặt chức năng này ở đâu đó mà thậm chí có thể ở các tệp khác nhau khiến chúng khó hơn tìm và thay đổi

Tôi đã viết toàn bộ trình thông dịch SQL để tôi có thể chuyển đổi tốt hơn từ MySQL sang MySQLi bằng PHP, vì tôi phải thay đổi trình thông dịch của mình và mọi thứ đều hoạt động, mặc dù đó là một ví dụ cực đoan.


-1

Để quyết định xem một mã có nên được sao chép hoặc di chuyển đến một hàm được gọi hai lần hay không, hãy thử xác định mã nào có thể xảy ra hơn:

  1. Sẽ cần phải thay đổi cả hai cách sử dụng mã theo cùng một cách.

  2. Sẽ cần phải thay đổi ít nhất một lần sử dụng mã để chúng khác nhau.

Trong trường hợp đầu tiên, sẽ tốt hơn nếu có một chức năng xử lý cả hai cách sử dụng; trong trường hợp sau, sẽ tốt hơn nếu có mã riêng cho hai cách sử dụng.

Khi quyết định liệu một đoạn mã sẽ được sử dụng một lần nên được viết thẳng hàng hay được kéo ra một hàm khác, hãy tìm hiểu cách người ta mô tả đầy đủ hành vi cần thiết của hàm. Nếu một mô tả đầy đủ và chính xác về hành vi cần thiết của hàm sẽ dài hơn hoặc dài hơn chính mã, việc di chuyển mã vào một hàm riêng biệt có thể khiến mọi thứ khó hiểu hơn là dễ dàng hơn. Có thể vẫn đáng làm nếu có khả năng cao người gọi thứ hai sẽ cần sử dụng cùng chức năng và mọi thay đổi trong tương lai của chức năng sẽ cần ảnh hưởng đến cả hai người gọi, nhưng nếu không có sự cân nhắc như vậy, khả năng đọc sẽ ưu tiên chia tách mọi thứ đến mức mà mã và một mô tả về hành vi cần thiết của nó sẽ dài bằng nhau.

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.