Chức năng nhỏ so với việc giữ chức năng phụ thuộc trong cùng chức năng


15

Tôi có một lớp thiết lập một mảng các nút và kết nối chúng với nhau trong một cấu trúc giống như biểu đồ. Là tốt nhất để:

  1. Giữ chức năng để khởi tạo và kết nối các nút trong một chức năng
  2. Có chức năng khởi tạo và kết nối ở hai chức năng khác nhau (và có thứ tự phụ thuộc mà các chức năng phải được gọi - mặc dù hãy nhớ rằng các chức năng này là riêng tư.)

Phương pháp 1: (Tệ ở chỗ một chức năng đang làm hai việc, NHƯNG nó giữ chức năng phụ thuộc được nhóm lại với nhau - các nút không bao giờ được kết nối mà không được khởi tạo trước.)

init() {
    setupNodes()
}

private func setupNodes() {
    // 1. Create array of nodes
    // 2. Go through array, connecting each node to its neighbors 
    //    according to some predefined constants
}

Phương pháp 2: (Theo nghĩa tốt hơn là nó tự ghi lại tài liệu, BUT connectNodes () không bao giờ được gọi trước setupNodes (), vì vậy bất kỳ ai làm việc với các bên trong lớp đều cần biết về thứ tự này.)

init() {
    setupNodes()
}

private func setupNodes() {
    createNodes()
    connectNodes()
}

private func createNodes() {
    // 1. Create array of nodes
}

private func connectNodes() {
    // 2. Go through array, connecting each node to its neighbors 
    //    according to some predefined constants
}

Vui mừng khi nghe bất kỳ suy nghĩ.



Một cách để giải quyết điều này bằng cách xác định các đối tượng trung gian chỉ có thể được sử dụng để tạo các đối tượng cuối cùng. Đây không phải lúc nào cũng là giải pháp phù hợp nhưng rất hữu ích nếu người dùng giao diện cần thao tác một trạng thái trung gian theo một cách nào đó.
Joel Cornett

Câu trả lời:


23

Vấn đề bạn đang giải quyết được gọi là khớp nối tạm thời

Bạn có quyền lo ngại về mức độ dễ hiểu của mã này:

private func setupNodes() {
    createNodes();
    connectNodes();
}

Tôi có thể đoán những gì đang xảy ra ở đó nhưng cho tôi biết nếu điều này làm cho những gì khác đang diễn ra rõ ràng hơn một chút:

private func setupNodes() {
    self.nodes = connectNodes( createNodes() );
}

Điều này có thêm lợi ích của việc ít kết hợp với sửa đổi các biến thể hiện nhưng với tôi có thể đọc được là số một.

Điều này làm cho connectNodes()sự phụ thuộc của các nút rõ ràng.


1
Cảm ơn các liên kết. Vì các hàm của tôi là riêng tư và được gọi từ hàm tạo - init () trong Swift - Tôi không nghĩ rằng mã của tôi sẽ tệ như các ví dụ mà bạn đã liên kết (không thể cho phép máy khách bên ngoài khởi tạo một thể hiện bằng một null dụ biến), nhưng tôi có một mùi tương tự.
mcfroob

1
Mã bạn đã thêm dễ đọc hơn, vì vậy tôi sẽ cấu trúc lại theo kiểu đó.
mcfroob

10

Các chức năng riêng biệt , vì hai lý do:

1. Các chức năng riêng tư là riêng tư cho chính xác tình huống này.

initChức năng của bạn là công khai và giao diện, hành vi và giá trị trả về là điều bạn cần lo lắng về việc bảo vệ và thay đổi. Kết quả mà bạn mong đợi từ phương pháp đó sẽ giống nhau cho dù bạn sử dụng triển khai nào.

Vì phần còn lại của chức năng được ẩn đằng sau từ khóa riêng tư đó, nên nó có thể được triển khai theo cách bạn muốn ... vì vậy bạn cũng có thể làm cho nó đẹp và mô đun, ngay cả khi một bit phụ thuộc vào cái khác được gọi trước.

2. Kết nối các nút với nhau có thể không phải là một chức năng riêng tư

Điều gì xảy ra nếu tại một số điểm bạn muốn thêm các nút khác vào mảng? Bạn có phá hủy thiết lập mà bạn có bây giờ và khởi tạo lại hoàn toàn không? Hoặc bạn thêm các nút vào mảng hiện có và sau đó chạy connectNodeslại?

connectNodesthể có một phản hồi lành mạnh nếu mảng các nút chưa được tạo (ném ngoại lệ? Trả về một tập hợp trống? Bạn phải quyết định điều gì hợp lý cho tình huống của bạn).


Tôi đã suy nghĩ theo cách tương tự như 1 và tôi có thể đưa ra một ngoại lệ hoặc thứ gì đó nếu các nút không được khởi tạo, nhưng nó không đặc biệt trực quan. Cảm ơn vì sự trả lời.
mcfroob

4

Bạn cũng có thể thấy (tùy thuộc vào mức độ phức tạp của từng nhiệm vụ này) rằng đây là một đường nối tốt để tách ra một lớp khác.

(Không chắc Swift có hoạt động theo cách này không nhưng mã giả :)

class YourClass {
    init(generator: NodesGenerator) {
        self.nodes = connectNodes(generator.make())
    }
    private func connectNodes() {

    }
}

class NodesGenerator {
    public func make() {
        // Return some nodes from storage or make new ones
    }
}

Điều này phân tách trách nhiệm tạo và sửa đổi các nút thành các lớp riêng biệt: NodeGeneratorchỉ quan tâm đến việc tạo / truy xuất các nút, trong khi YourClasschỉ quan tâm đến việc kết nối các nút mà nó được đưa ra.


2

Ngoài mục đích chính xác của các phương thức riêng tư, Swift còn cung cấp cho bạn khả năng sử dụng các chức năng bên trong.

Các phương thức bên trong là hoàn hảo cho các chức năng chỉ có một trang web cuộc gọi duy nhất, nhưng cảm giác như chúng không biện minh là các chức năng riêng tư riêng biệt.

Ví dụ, rất phổ biến khi có chức năng "nhập" đệ quy công khai, kiểm tra các điều kiện tiên quyết, thiết lập một số tham số và ủy quyền cho một hàm đệ quy riêng thực hiện công việc.

Đây là một ví dụ về cách nó có thể trông trong trường hợp này:

init() {
    self.nodes = setupNodes()

    func setupNodes() {
        var nodes = createNodes()
        connect(Nodes: nodes)
    }

    private func createNodes() -> [Node]{
        // 1. Create array of nodes
    }

    func connect(Nodes: [Node]) {
        // 2. Go through array, connecting each node to its neighbors 
        //    according to some predefined constants
    }
}

Hãy chú ý đến cách tôi sử dụng các giá trị và tham số trả về để truyền xung quanh dữ liệu, thay vì thay đổi trạng thái chia sẻ. Điều này làm cho luồng dữ liệu rõ ràng hơn nhiều từ cái nhìn đầu tiên, mà không cần phải nhảy vào thực hiện.


0

Mỗi chức năng bạn khai báo mang theo gánh nặng của việc thêm tài liệu và làm cho nó được khái quát hóa để có thể sử dụng được bởi các phần khác của chương trình. Nó cũng mang gánh nặng hiểu cách các chức năng khác trong tệp có thể sử dụng nó cho ai đó đọc mã.

Tuy nhiên, nếu nó không được sử dụng bởi các phần khác trong chương trình của bạn, tôi sẽ không thể hiện nó như một chức năng riêng biệt.

Nếu ngôn ngữ của bạn hỗ trợ nó, bạn vẫn có thể có một chức năng một việc bằng cách sử dụng các chức năng lồng nhau

function setupNodes ()  {
  function createNodes ()  {...} 
  function connectNodes ()  {...}
  createNodes() 
  connectNodes() 
} 

Vị trí khai báo rất quan trọng, và trong ví dụ trên, rõ ràng mà không cần thêm bất kỳ manh mối nào cho thấy các chức năng bên trong chỉ được sử dụng trong cơ thể của chức năng bên ngoài.

Ngay cả khi bạn khai báo chúng là các hàm riêng tư, tôi giả sử rằng chúng vẫn hiển thị với toàn bộ tệp. Vì vậy, bạn sẽ cần khai báo chúng gần với khai báo của hàm chính và thêm một số tài liệu làm rõ rằng chúng chỉ được sử dụng bởi hàm ngoài.

Tôi không nghĩ rằng bạn phải nghiêm túc làm cái này hay cái khác. Điều tốt nhất để làm khác nhau tùy theo từng trường hợp.

Việc chia nó thành nhiều chức năng chắc chắn sẽ tăng thêm sự hiểu biết tại sao có 3 chức năng và tất cả chúng hoạt động với nhau như thế nào, nhưng nếu logic phức tạp thì chi phí bổ sung này có thể ít hơn nhiều so với sự đơn giản được đưa ra bằng cách phá vỡ logic phức tạp thành những phần đơn giản hơn.


Lựa chọn thú vị. Như bạn nói, tôi nghĩ rằng nó có thể hơi khó hiểu về lý do tại sao hàm được khai báo như thế này, nhưng nó sẽ giữ cho phụ thuộc hàm được chứa đầy đủ.
mcfroob

Để trả lời một số điều không chắc chắn trong câu hỏi này: 1) Có, Swift hỗ trợ các chức năng bên trong và 2) Nó có hai cấp độ "riêng tư". privatechỉ cho phép truy cập trong loại kèm theo (struct / class / enum), trong khi fileprivatecho phép truy cập trong toàn bộ tệp
Alexander - Phục hồi Monica
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.