Sao chép mã không có sự trừu tượng rõ ràng


14

Bạn đã bao giờ gặp phải trường hợp sao chép mã trong đó, khi nhìn vào các dòng mã, bạn không thể phù hợp với sự trừu tượng theo chủ đề với nó mô tả trung thực vai trò của nó trong logic? Và bạn đã làm gì để giải quyết nó?

Đó là sao chép mã, vì vậy lý tưởng nhất là chúng ta cần thực hiện một số khúc xạ, ví dụ như làm cho nó trở thành chức năng của riêng nó. Nhưng vì mã không có một sự trừu tượng hóa tốt để mô tả nó, kết quả sẽ là một hàm lạ mà chúng ta thậm chí không thể tìm ra một cái tên hay, và vai trò của nó trong logic không rõ ràng chỉ khi nhìn vào nó. Điều đó, với tôi, làm tổn thương sự rõ ràng của mã. Chúng ta có thể bảo tồn sự rõ ràng và để nó như vậy nhưng sau đó chúng ta làm tổn thương khả năng bảo trì.

Bạn nghĩ gì là cách tốt nhất để giải quyết một cái gì đó như thế này?

Câu trả lời:


18

Đôi khi sao chép mã là kết quả của một "cách chơi chữ": Hai thứ trông giống nhau, nhưng không.

Có thể là quá trừu tượng hóa có thể phá vỡ mô-đun thực sự của hệ thống của bạn. Theo chế độ mô đun hóa, bạn phải quyết định "điều gì có khả năng thay đổi?" và "cái gì ổn định?". Bất cứ thứ gì ổn định đều được đặt trong giao diện, trong khi bất cứ thứ gì không ổn định đều được gói gọn trong quá trình thực hiện của mô-đun. Sau đó, khi mọi thứ thay đổi, thay đổi bạn cần thực hiện được tách biệt với mô-đun đó.

Tái cấu trúc là cần thiết khi những gì bạn nghĩ là ổn định (ví dụ: lệnh gọi API này sẽ luôn có hai đối số) cần thay đổi.

Vì vậy, đối với hai đoạn mã trùng lặp này, tôi sẽ hỏi: Liệu một thay đổi bắt buộc đối với một điều này có nhất thiết phải thay đổi khác không?

Cách bạn trả lời câu hỏi đó có thể giúp bạn hiểu rõ hơn về sự trừu tượng tốt có thể là gì.

Các mẫu thiết kế cũng là công cụ hữu ích. Có lẽ mã trùng lặp của bạn đang thực hiện truyền tải một số dạng và nên áp dụng mẫu lặp.

Nếu mã trùng lặp của bạn có nhiều giá trị trả về (và đó là lý do tại sao bạn không thể thực hiện một phương thức trích xuất đơn giản), thì có lẽ bạn nên tạo một lớp giữ các giá trị được trả về. Lớp có thể gọi một phương thức trừu tượng cho mỗi điểm khác nhau giữa hai đoạn mã. Sau đó, bạn sẽ thực hiện hai triển khai cụ thể của lớp: một cho mỗi phân đoạn. [Đây thực sự là mẫu thiết kế Phương thức mẫu, không bị nhầm lẫn với khái niệm mẫu trong C ++. Ngoài ra, những gì bạn đang xem có thể được giải quyết tốt hơn với mẫu Chiến lược.]

Một cách tự nhiên và hữu ích khác để suy nghĩ về nó là với các hàm bậc cao hơn. Ví dụ, tạo lambdas hoặc sử dụng các lớp bên trong ẩn danh để mã chuyển sang trừu tượng hóa. Nói chung, bạn có thể loại bỏ trùng lặp, nhưng trừ khi thực sự có mối quan hệ giữa chúng [nếu một thay đổi, do đó phải thay đổi] thì bạn có thể bị tổn thương mô-đun, không giúp đỡ nó.


4

Khi bạn gặp phải một tình huống như thế này, tốt nhất bạn nên nghĩ về sự trừu tượng "phi truyền thống". Có thể bạn có nhiều sự trùng lặp trong một hàm và bao hàm một hàm cũ đơn giản không phù hợp lắm vì bạn phải truyền quá nhiều biến. Ở đây, một hàm lồng nhau kiểu D / Python (có quyền truy cập vào phạm vi bên ngoài) sẽ hoạt động rất tốt. (Vâng, bạn có thể tạo một lớp để giữ tất cả trạng thái đó, nhưng nếu bạn chỉ sử dụng nó trong hai chức năng, thì đây là một cách giải quyết xấu xí và dài dòng vì không có các hàm lồng nhau.) mixin sẽ hoạt động tốt. Có lẽ những gì bạn thực sự cần là một vĩ mô. Có lẽ bạn nên xem xét một số siêu lập trình mẫu hoặc phản xạ / hướng nội, hoặc thậm chí lập trình tổng quát.

Tất nhiên, từ quan điểm thực dụng, tất cả đều khó khăn nếu không thể làm được nếu ngôn ngữ của bạn không hỗ trợ chúng và không có đủ khả năng lập trình siêu dữ liệu để thực hiện chúng một cách sạch sẽ trong ngôn ngữ. Nếu đây là trường hợp, tôi không biết phải nói gì với bạn ngoại trừ "có được ngôn ngữ tốt hơn". Ngoài ra, học một ngôn ngữ cấp cao với nhiều khả năng trừu tượng hóa (như Ruby, Python, Lisp hoặc D) có thể giúp bạn lập trình tốt hơn trong các ngôn ngữ cấp thấp hơn trong đó một số kỹ thuật vẫn có thể sử dụng được, nhưng ít rõ ràng hơn.


+1 cho rất nhiều kỹ thuật xuất sắc được nén trong một không gian chật hẹp. (Chà, cũng sẽ là +1 cho các kỹ thuật được mô tả.)
Macneil

3

Cá nhân tôi bỏ qua nó và đi tiếp. Rất có thể nếu đó là một trường hợp kỳ quặc thì tốt hơn là sao chép nó, bạn có thể dành thời gian tái cấu trúc và nhà phát triển tiếp theo sẽ xem xét và hoàn tác thay đổi của bạn!


2

Không có mẫu mã, thật khó để nói tại sao mã của bạn không có sự trừu tượng hóa dễ nhận biết. Với lời cảnh báo đó, đây là một vài ý tưởng:

  • thay vì tạo một chức năng mới để giữ mã chung, hãy chia chức năng thành nhiều phần riêng biệt;
  • nhóm các mảnh nhỏ lại với nhau dựa trên các loại dữ liệu phổ biến hoặc hành vi trừu tượng;
  • viết lại mã trùng lặp cho các mảnh mới;
  • nếu mã mới vẫn bất chấp sự trừu tượng rõ ràng, hãy chia nó thành nhỏ và lặp lại quy trình.

Khó khăn lớn nhất trong bài tập này là chức năng của bạn có khả năng kết hợp quá nhiều hành vi không liên quan ở mức độ trừu tượng nhất định và bạn cần xử lý một số trong số chúng ở mức thấp hơn. Bạn phỏng đoán chính xác rằng sự rõ ràng là chìa khóa để duy trì mã, nhưng làm cho hành vi của mã rõ ràng (điều kiện hiện tại của nó) rất khác với việc làm cho ý định của mã rõ ràng.

Làm cho cách thức các đoạn mã nhỏ hơn trở nên trừu tượng bằng cách có chữ ký hàm của chúng xác định cái gì và các mảnh lớn hơn sẽ dễ phân loại hơn.

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.