Nó có hữu ích với mã tái cấu trúc mini với hy vọng cải thiện chất lượng không, hay nó chỉ là mã di chuyển của xung quanh mà không mang lại nhiều lợi ích?


12

Thí dụ

Tôi đã bắt gặp mã nguyên khối thực hiện "mọi thứ" ở một nơi - tải dữ liệu từ cơ sở dữ liệu, hiển thị đánh dấu HTML, hoạt động như một bộ định tuyến / bộ điều khiển / hành động. Tôi bắt đầu áp dụng mã cơ sở dữ liệu di chuyển SRP vào tệp riêng của mình, cung cấp cách đặt tên tốt hơn cho mọi thứ và tất cả đều có vẻ tốt, nhưng sau đó tôi bắt đầu nghi ngờ về lý do tại sao tôi làm việc này.

Tại sao phải tái cấu trúc? Mục đích là gì? Có phải nó vô dụng? Lợi ích là gì? Lưu ý rằng tôi hầu như chỉ để lại tệp nguyên khối, nhưng chỉ tái cấu trúc phần nhỏ hơn có liên quan đến khu vực mà tôi cần thực hiện một số công việc.

Mã gốc:

Để đưa ra một ví dụ cụ thể, tôi đã xem qua đoạn mã này - nó tải các thông số kỹ thuật của sản phẩm theo id sản phẩm đã biết hoặc bởi id phiên bản do người dùng chọn:

if ($verid)
    $sql1 = "SELECT * FROM product_spec WHERE id = " . clean_input($verid);
else
    $sql1 = "SELECT * FROM product_spec WHERE product_id = " . clean_input($productid) ;
$result1 = query($sql1);
$row1 = fetch_array($result1);
/* html markup follows */

Tái cấu trúc:

Vì tôi đang thực hiện một số công việc yêu cầu tôi thay đổi mọi thứ trong phần mã cụ thể này, tôi đã thay đổi nó để sử dụng mẫu kho lưu trữ và nâng cấp nó để sử dụng các tiện ích MySQL hướng đối tượng:

//some implementation details omitted 
$this->repository = new SpecRepository($mysql);
if ($verid)
    $row1 = $this->repository->getSpecByVersion($verid);
else 
    $row1 = $this->repository->getSpecByProductId($productid);
/* html markup follows to be refactored or left alone till another time*/

//added new class:
class SpecRepository extends MySqlRepository
{

    function getSpecByVersion(int $verid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE id = ?
        ", $verid)->getSingleArray();
    }

    function getSpecByProductId(int $productid)
    {
        return $this->getMySql()->paramQuery("
            SELECT * FROM product_spec WHERE product_id = ?
        ", $productid)->getSingleArray();
    }
}

Tôi có nên làm điều này?

Nhìn lại các thay đổi, mã vẫn còn đó, mã có cùng chức năng, nhưng trong các tệp khác nhau, tên, địa điểm khác nhau, sử dụng kiểu hướng đối tượng hơn là theo thủ tục. Trên thực tế, thật buồn cười khi lưu ý rằng mã được cấu trúc lại trông có vẻ bồng bềnh hơn rất nhiều mặc dù có cùng chức năng.

Tôi thấy trước một số câu trả lời rằng "nếu bạn không biết lý do tại sao bạn tái cấu trúc, đừng làm điều đó", và có lẽ tôi có thể đồng ý. Lý do của tôi là để cải thiện chất lượng mã theo thời gian (và hy vọng của tôi là tôi sẽ làm như vậy bằng cách tuân theo SRP và các nguyên tắc khác).

Là những lý do đủ tốt hay tôi đang lãng phí thời gian vào việc "sắp xếp lại mã xung quanh" theo cách này? Nói chung, việc tái cấu trúc này có cảm giác hơi giống với việc giẫm nước thành thật - nó cần có thời gian và nó trở nên "tách biệt" hơn theo SRP nhưng mặc dù ý định tốt của tôi, tôi không cảm thấy mình đang cải thiện đáng kinh ngạc. Do đó, tranh luận nếu tốt nhất là để lại mã như trước đây và không tái cấu trúc.

Tại sao tôi lại cấu trúc lại ở nơi đầu tiên?

Trong trường hợp của tôi, tôi đang thêm chức năng mới cho một dòng sản phẩm mới, vì vậy tôi phải tuân theo cấu trúc mã hiện có cho các dòng sản phẩm tương tự hoặc tự viết.


OT nhưng một cái gì đó để xem xét là Select * from ..có thể được coi là một mô hình chống. Xem stackoverflow.com/q/3639861/31326
Peter M

1
@PeterM: Trừ khi bạn viết ORM, trong trường hợp đó select *trở thành "cách thực hành tốt nhất".
Robert Harvey

@RobertHarvey Trong trường hợp nào tôi đang đặt câu hỏi tại sao bạn lại viết ORM của riêng mình: D
Peter M

Tôi đã thực hiện tái cấu trúc cho các nguyên nhân không liên quan hơn. Cắt giảm nợ kỹ thuật là tốt ngay khi lợi ích vượt xa chi phí. Hình như đây là trường hợp của bạn. Câu hỏi là: Bạn có các bài kiểm tra phù hợp với bạn để đảm bảo rằng mã vẫn hoạt động như mong đợi?
Laiv

1
Độ tin cậy phải là số liệu chiếm ưu thế khi tái cấu trúc IMO, không phải khả năng duy trì liên quan đến tính thay đổi, vì độ tin cậy làm giảm lý do khiến mọi thứ thay đổi ngay từ đầu. Nó cải thiện tính ổn định và mã hoàn toàn ổn định và đáng tin cậy và đáp ứng tất cả những gì nó cần để làm phát sinh ít chi phí bảo trì, vì nó có rất ít lý do để thay đổi. Tìm cách giảm bớt lý do để mọi thứ thay đổi thay vì cố gắng làm cho chúng dễ dàng nhất có thể thay đổi. Tìm kiếm cái trước cũng sẽ có xu hướng đưa ra một số cái sau.

Câu trả lời:


13

Trong ví dụ cụ thể mà bạn đã đưa ra, tái cấu trúc có thể không cần thiết, ít nhất là chưa. Nhưng bạn đã thấy rằng có một tiềm năng cho mã lộn xộn trong tương lai sẽ dẫn đến việc tái cấu trúc dài hơn và tẻ nhạt hơn, vì vậy bạn đã chủ động và dọn dẹp mọi thứ ngay bây giờ.

Tôi cũng sẽ nói rằng lợi thế bạn đạt được ở đây là mã dễ hiểu hơn, ít nhất là theo quan điểm của tôi, vì một người không biết cơ sở mã của bạn.


Nói chung:

Việc tái cấu trúc có giúp các nhà phát triển khác hiểu mã của bạn dễ dàng hơn không?

Liệu tái cấu trúc làm cho nó dễ dàng hơn để tăng cường mã?

Việc tái cấu trúc có giúp duy trì mã dễ dàng hơn không?

Liệu tái cấu trúc làm cho nó dễ dàng hơn để gỡ lỗi?

Nếu câu trả lời cho bất kỳ câu hỏi nào là "có", thì nó có đáng không, và nó không chỉ là "di chuyển mã xung quanh?

Nếu câu trả lời cho tất cả những điều này là "không", thì có lẽ nó không cần phải được tái cấu trúc.

Kích thước cuối cùng của cơ sở mã thể lớn hơn về LoC (mặc dù một số phép tái cấu trúc có thể thu hẹp cơ sở mã bằng cách hợp lý hóa mã dự phòng), nhưng đó không phải là một yếu tố nếu có lợi ích khác.


9

Tái cấu trúc sẽ phình to nếu bạn tháo rời một khối nguyên khối.

Tuy nhiên, bạn đã tìm thấy lợi ích.

"Trong trường hợp của tôi, tôi đang thêm chức năng mới cho một dòng sản phẩm mới."

Bạn không thể thêm chức năng cho một khối nguyên khối rất dễ dàng mà không phá vỡ mọi thứ.

Bạn nói rằng cùng một mã đang lấy dữ liệu và thực hiện đánh dấu. Tuyệt vời, cho đến khi bạn muốn thay đổi cột mà bạn đang chọn và thao tác để hiển thị trong một số điều kiện nhất định. Đột nhiên, mã đánh dấu của bạn ngừng hoạt động trong một trường hợp nhất định và bạn phải cấu trúc lại.


có, nhưng tôi cũng có thể tiếp tục thêm vào khối ngay cả đối với chức năng mới :) tức là tôi sẽ thêm một if/elsekhối mới và làm theo kiểu mã ban đầu để thêm các câu lệnh SQL, và sau đó tôi sẽ đặt đánh dấu HTML mới vào cùng một tệp nguyên khối. Và để thay đổi các cột, tôi sẽ tìm khối if / other chịu trách nhiệm cho các dòng SQL chứa và chỉnh sửa SQL đó để thay đổi các cột, mà không phá vỡ tệp đó.
Dennis

Với cách tiếp cận được cấu trúc lại, trước tiên tôi có thể đi đến khối if / other trong khối để thấy rằng tôi cần phải đi đến tệp kho lưu trữ riêng để thay đổi SQL. Hoặc nếu tôi đã làm việc với khối nguyên khối gần đây, tôi sẽ biết đi ngay vào tệp kho lưu trữ, bỏ qua khối nguyên khối. Hoặc nếu tôi đã tìm kiếm cơ sở mã của mình cho SQL cụ thể, tìm kiếm sẽ đưa tôi vào tệp kho lưu trữ mới cũng bỏ qua khối nguyên khối
Dennis

2

Tôi xem xét tái cấu trúc nếu tôi biết rằng tôi sẽ phải làm việc hàng tháng hoặc thậm chí nhiều năm với dự án đó. Nếu tôi phải thực hiện một thay đổi ở đây và ở đó, hơn là tôi tự chống lại sự thôi thúc di chuyển mã xung quanh.

Cách thức tái cấu trúc ưa thích là khi tôi có mã cơ sở với một số loại thử nghiệm tự động. Mặc dù vậy tôi phải rất cẩn thận với những gì tôi thay đổi để đảm bảo mọi thứ đang hoạt động như trước. Bạn sẽ mất niềm tin rất sớm nếu bạn cung cấp các lỗi trong các lĩnh vực khác ngoài lĩnh vực mà bạn phải làm việc.

Tôi đánh giá tái cấu trúc vì những lý do sau:

  • Tôi hiểu mã hơn
  • Tôi xóa mã trùng lặp để nếu tôi gặp lỗi ở một nơi, tôi không phải tìm mọi nơi mã đã được sao chép
  • Tôi tạo các lớp có vai trò được xác định rõ. Ví dụ: tôi có thể sử dụng lại cùng một kho lưu trữ để hydrat hóa trang HTML, nguồn cấp dữ liệu XML hoặc công việc nền. Điều này sẽ không thể xảy ra nếu mã truy cập cơ sở dữ liệu chỉ tồn tại trong lớp HTML
  • Tôi đổi tên các biến, phương thức, lớp, mô-đun, thư mục nếu chúng không khớp với thực tế hiện tại; Tôi commentmã thông qua tên biến, tên phương thức
  • Tôi giải quyết các lỗi phức tạp với sự kết hợp giữa kiểm tra đơn vị và tái cấu trúc
  • Tôi có sự linh hoạt để thay thế toàn bộ mô-đun, gói. Nếu ORM hiện tại đã lỗi thời, lỗi hoặc không được bảo trì, thì việc thay thế nó sẽ dễ dàng hơn nếu nó không được trải rộng trên toàn dự án.
  • Tôi khám phá các khả năng hoặc cải tiến mới dẫn đến các đề xuất cho khách hàng.
  • Tôi đã tạo ra các thành phần có thể tái sử dụng. Đây là một trong những lợi ích lớn nhất vì công việc được thực hiện trong dự án đó có thể đã được sử dụng lại cho một khách hàng / dự án khác.
  • Tôi thường bắt đầu với giải pháp ngu ngốc nhất, mã hóa cứng, lập dị và tôi tái cấu trúc nó sau khi tôi tìm hiểu thêm. Khoảnh khắc sau này có thể là hôm nay hoặc có thể sau vài ngày. Tôi không muốn dành quá nhiều thời gian cho architectgiải pháp. Điều này sẽ đến trong khi viết - viết lại mã qua lại.

Trong một thế giới tái cấu trúc hoàn hảo nên được xác minh bằng các bài kiểm tra đơn vị, kiểm tra tích hợp, v.v. Nếu không có, hãy thử thêm chúng. Ngoài ra một số IDE có thể giúp rất nhiều. Tôi thường sử dụng một plugin có chi phí tiền. Tôi muốn dành ít thời gian với tái cấu trúc và rất hiệu quả về nó.

Tôi cũng giới thiệu lỗi. Tôi có thể thấy lý do tại sao một số QA đang hỏi are you guys doing refactoring? because everything stopped working!Đây là rủi ro mà nhóm phải gánh chịu và chúng tôi luôn phải minh bạch về điều đó.

Trong những năm qua, tôi thấy rằng tái cấu trúc liên tục đã cải thiện năng suất của tôi. Đã dễ dàng hơn nhiều để thêm các tính năng mới. Ngoài ra, việc xây dựng lại lớn không yêu cầu viết lại toàn bộ như thường xảy ra khi cơ sở mã không thích ứng với sự phát triển của sản phẩm. Ngoài ra nó rất vui.


Điều này rất gần với một cuộc bỏ phiếu - Không có 'Tôi' trong 'Lập trình viên máy tính'. Bởi 'Tôi' làm bạn tôi 'Tôi' (bỏ phiếu) hoặc bạn có nghĩa là các nhà phát triển làm việc với mã hiện tại và trong tương lai?
mattnz

Tôi cũng nhận thấy nó, nhưng tôi đưa nó vào từ ngữ. Ngoài ra tôi đã làm việc như một nhà phát triển solo trong nhiều năm và khi tôi viết câu trả lời này, tôi đã nhắc lại chủ yếu từ kinh nghiệm đó. Tôi có thể thay thế Ibằng todù.
Adrian Iftode

1

Tôi nghĩ rằng câu hỏi bạn phải tự hỏi mình không quá nhiều "Có lợi ích gì không?" nhưng "Ai sẽ trả tiền cho công việc này?"

Tất cả chúng ta đều có thể tranh luận về lợi ích nhận được cho đến cuối ngày, nhưng trừ khi bạn có một khách hàng tin vào những lợi ích đó và giá trị của chúng đủ để trả cho những thay đổi bạn không nên thực hiện.

Thật dễ dàng khi bạn ở trong nhóm phát triển ở đâu đó để xem mã và muốn cấu trúc lại nó để làm cho nó 'tốt hơn'. Nhưng đặt một giá trị vào 'mã tốt hơn' khi sản phẩm đã hoạt động thực sự khó khăn.

Những thứ như 'phát triển nhanh hơn các tính năng mới' không cắt giảm vì chúng chỉ giảm chi phí. Bạn cần tạo doanh số.


1

Theo tôi, tái cấu trúc là một khoản đầu tư tốt.

Nếu không, bạn sẽ sớm nhận được các khoản nợ kỹ thuật (các vấn đề chưa được giải quyết, mã xấu chỉ hoạt động trong một số điều kiện nhất định, v.v.). Các khoản nợ kỹ thuật sẽ sớm ngày càng lớn hơn, cho đến khi phần mềm không còn bảo trì được nữa.

Nhưng để thực hiện tái cấu trúc công việc, bạn cũng phải đầu tư vào các bài kiểm tra. Bạn nên tạo các bài kiểm tra tự động, lý tưởng nhất là bạn nên có bài kiểm tra đơn vị và bài kiểm tra tích hợp. Điều này giúp bạn không phá vỡ mã hiện có.

Nếu bạn phải thuyết phục sếp hoặc đồng nghiệp của mình, bạn nên đọc một số cuốn sách về các phương pháp nhanh (ví dụ SCRUM) và phát triển theo hướng kiểm tra.

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.