Tái cấu trúc phương thức dài: để nguyên so với tách thành các phương thức so với sử dụng các hàm cục bộ


9

Giả sử tôi có phương pháp dài như thế này:

public void SomeLongMethod()
{
    // Some task #1
    ...

    // Some task #2
    ...
}

Phương pháp này không có bất kỳ phần lặp đi lặp lại nào nên được chuyển sang phương thức riêng biệt hoặc chức năng cục bộ.

Có nhiều người (bao gồm cả tôi) nghĩ rằng các phương thức dài là mùi mã. Ngoài ra tôi không thích ý tưởng sử dụng #region(các) ở đây và có một câu trả lời rất phổ biến giải thích tại sao điều này là xấu .


Nhưng nếu tôi tách mã này thành các phương thức

public void SomeLongMethod()
{
    Task1();

    Task2();
}

private void Task1()
{
    // Some task #1
    ...
}

private void Task2()
{
    // Some task #1
    ...
}

Tôi thấy các vấn đề sau:

  • Phạm vi định nghĩa lớp gây ô nhiễm với các định nghĩa được sử dụng bên trong bởi một phương thức duy nhất và điều đó có nghĩa là tôi nên ghi lại ở đâu đó Task1Task2chỉ dành cho nội bộ của SomeLongMethod(hoặc mọi người đọc mã của tôi sẽ phải suy ra ý tưởng này).

  • Ô nhiễm IDE tự động hoàn thành (ví dụ Intellisense) các phương thức sẽ chỉ được sử dụng một lần trong SomeLongMethodphương thức duy nhất .


Vì vậy, nếu tôi tách mã phương thức này thành các hàm cục bộ

public void SomeLongMethod()
{
    Task1();

    Task2();

    void Task1()
    {
        // Some task #1
        ...
    }

    void Task2()
    {
        // Some task #1
        ...
    }
}

sau đó, điều này không có nhược điểm của các phương thức riêng biệt nhưng điều này trông không tốt hơn (ít nhất là đối với tôi) so với phương pháp ban đầu.


Phiên bản nào SomeLongMethoddễ bảo trì và dễ đọc hơn cho bạn và tại sao?



@gnat câu hỏi của tôi là dành riêng cho c # , không phải java . Mối quan tâm chính của tôi là về phương thức cổ điển so với chức năng cục bộ, không chỉ tách biệt thành các phương thức hay không.
Vadim Ovchinnikov

@gnat Ngoài ra AFAIK Java (không giống như C #) không hỗ trợ các hàm lồng nhau.
Vadim Ovchinnikov

1
từ khóa riêng chắc chắn bảo vệ 'phạm vi định nghĩa lớp' của bạn?
Ewan

1
chờ đã ...... lớp học của bạn lớn như thế nào?
Ewan

Câu trả lời:


13

Các tác vụ độc lập nên được chuyển sang các phương thức khép kín. Không có nhược điểm đủ lớn để ghi đè lên mục tiêu này.

Bạn nói rằng họ làm ô nhiễm không gian tên của lớp, nhưng đó không phải là sự đánh đổi mà bạn phải xem xét khi bao thanh toán mã của bạn. Một người đọc tương lai của lớp (ví dụ như bạn) có nhiều khả năng hỏi " Phương pháp cụ thể này làm gì và tôi phải thay đổi nó như thế nào để nó thực hiện những gì các yêu cầu đã thay đổi nói?" so với "Mục đích và ý nghĩa của tất cả các phương thức trong lớp là gì? " Do đó, làm cho mỗi phương thức dễ hiểu hơn (bằng cách làm cho nó có ít phần tử hơn) quan trọng hơn nhiều so với việc làm cho toàn bộ lớp dễ hiểu hơn (bằng cách làm cho nó có ít phương thức hơn).

Tất nhiên, nếu các tác vụ không thực sự khép kín nhưng yêu cầu một số biến trạng thái di chuyển cùng, thì một đối tượng phương thức, đối tượng trợ giúp hoặc một cấu trúc tương tự có thể là lựa chọn đúng thay thế. Và nếu các tác vụ hoàn toàn khép kín hoàn toàn không bị ràng buộc với miền ứng dụng (ví dụ: chỉ định dạng chuỗi, thì có thể hình dung chúng nên đi vào một lớp (tiện ích) hoàn toàn khác. Nhưng tất cả đều không thay đổi thực tế rằng " giữ phương pháp cho mỗi lớp xuống "tốt nhất là một mục tiêu rất phụ.


Vì vậy, bạn là cho các phương thức, không phải chức năng địa phương, có?
Vadim Ovchinnikov

2
@VadimOvchinnikov Với một IDE gấp mã hiện đại, có rất ít sự khác biệt. Ưu điểm lớn nhất của hàm cục bộ là nó có thể truy cập các biến mà bạn phải truyền theo dưới dạng tham số, nhưng nếu có nhiều trong số đó, thì các tác vụ không thực sự độc lập để bắt đầu.
Kilian Foth

6

Tôi không thích một trong hai cách tiếp cận. Bạn đã không cung cấp bất kỳ bối cảnh rộng lớn hơn nhưng hầu hết các lần nó trông như thế này:

Bạn có một lớp thực hiện một số công việc bằng cách kết hợp sức mạnh của nhiều dịch vụ thành một kết quả duy nhất.

class SomeClass
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;

    public void SomeLongMethod()
    {
        _service1.Task1();
        _service2.Task2();       
    }
}

Mỗi dịch vụ của bạn chỉ thực hiện một phần của logic. Một cái gì đó đặc biệt, mà chỉ có người hầu này có thể làm. Nếu nó có các phương thức riêng tư khác ngoài các phương thức hỗ trợ API chính để bạn có thể sắp xếp chúng dễ dàng và dù sao cũng không nên có quá nhiều phương thức.

class Service1
{
    public void Task1()
    {
        // Some task #1
        ...
    }

    private void SomeHelperMethod() {}
}

class Service2
{
    void Task2()
    {
        // Some task #1
        ...
    }
}

Điểm mấu chốt là: nếu bạn bắt đầu tạo các vùng trong mã của mình để áp dụng các phương thức gorup, đã đến lúc bắt đầu suy nghĩ về việc đóng gói logic của chúng bằng một dịch vụ mới.


2

Vấn đề với việc tạo các hàm trong phương thức là nó không làm giảm độ dài của phương thức.

Nếu bạn muốn mức độ bảo vệ bổ sung 'riêng tư cho phương thức' thì bạn có thể tạo một lớp mới

public class BigClass
{
    public void SomeLongMethod();

    private class SomeLongMethodRunner
    {
        private void Task1();
        private void Task2();
    }
}

Nhưng các lớp học trong lớp là một điều xấu xí lớn: /


2

Tối thiểu hóa phạm vi của các cấu trúc ngôn ngữ như các biến và phương thức là thực hành tốt. Ví dụ tránh các biến toàn cục .. Nó làm cho mã dễ hiểu hơn và giúp tránh sử dụng sai vì cấu trúc có thể được truy cập ở ít nơi hơn.

Điều này nói về việc sử dụng các chức năng địa phương.


1
hmm chắc chắn sử dụng lại mã == chia sẻ một phương thức giữa nhiều người gọi. tức là tối đa hóa phạm vi của nó
Ewan

1

Tôi đồng ý rằng các phương thức dài thường là mùi mã, nhưng tôi không nghĩ đó là toàn bộ hình ảnh, thay vào đó là lý do tại sao phương thức này dài cho thấy có vấn đề. Quy tắc chung của tôi là nếu mã đang thực hiện nhiều tác vụ riêng biệt thì mỗi tác vụ đó phải là phương thức riêng của nó. Đối với tôi, việc các phần mã đó có lặp đi lặp lại hay không; trừ khi bạn thực sự chắc chắn rằng không ai muốn gọi riêng chúng trong tương lai (và đó là một điều rất khó để chắc chắn!) đặt chúng vào một phương thức khác (và đặt nó ở chế độ riêng tư - đó phải là một chỉ báo rất mạnh bạn không mong đợi nó sẽ được sử dụng bên ngoài lớp học).

Tôi phải thú nhận rằng tôi chưa sử dụng các chức năng cục bộ, vì vậy sự hiểu biết của tôi chưa hoàn chỉnh ở đây, nhưng liên kết bạn cung cấp cho thấy chúng thường được sử dụng theo cách tương tự như biểu thức lambda (mặc dù đây là một số trường hợp sử dụng nơi chúng có thể phù hợp hơn lambdas hoặc nơi bạn không thể sử dụng lambda xem các hàm Local so với biểu thức lambda để biết chi tiết). Sau đó, tôi nghĩ rằng nó có thể giảm đến mức độ chi tiết của các nhiệm vụ 'khác'; Nếu chúng khá tầm thường thì có lẽ một chức năng cục bộ sẽ ổn chứ? Mặt khác, tôi đã thấy các ví dụ về các biểu thức lambda khổng lồ (có lẽ là nơi một nhà phát triển gần đây đã phát hiện ra LINQ), điều này làm cho mã trở nên dễ hiểu và dễ bảo trì hơn so với việc nó được gọi đơn giản từ một phương thức khác.

Các chức năng địa phương trang khẳng định rằng:

'Các chức năng cục bộ làm cho ý định mã của bạn rõ ràng. Bất cứ ai đọc mã của bạn đều có thể thấy rằng phương thức này không thể gọi được ngoại trừ phương thức chứa. Đối với các dự án nhóm, họ cũng khiến cho một nhà phát triển khác không thể gọi nhầm phương thức trực tiếp từ nơi khác trong lớp hoặc quy tắc. '

Tôi không chắc chắn tôi thực sự với MS về điều này là một lý do để sử dụng. Phạm vi phương thức của bạn (cùng với tên và nhận xét của nó) sẽ làm cho nó rõ ràng ý định của bạn lúc đó là gì . Với Chức năng cục bộ tôi có thể thấy rằng, các nhà phát triển khác có thể thấy nó là bất khả xâm phạm hơn, nhưng có khả năng đến mức nếu một thay đổi xảy ra trong tương lai đòi hỏi chức năng đó phải được sử dụng lại, họ viết thủ tục riêng và rời khỏi Chức năng cục bộ ; do đó tạo ra một vấn đề sao chép mã. Câu sau của trích dẫn trên có thể có liên quan nếu bạn không tin tưởng các thành viên trong nhóm hiểu một phương pháp riêng tư trước khi gọi nó, và vì vậy hãy gọi nó không phù hợp (vì vậy quyết định của bạn có thể bị ảnh hưởng bởi điều đó, mặc dù giáo dục sẽ là một lựa chọn tốt hơn ở đây) .

Tóm lại, nói chung, tôi thích tách ra thành các phương thức nhưng MS đã viết Hàm địa phương vì một lý do, vì vậy tôi chắc chắn sẽ không nói không sử dụng chúng,

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.