작은 기능 대 종속 기능을 동일한 기능으로 유지


15

노드 배열을 설정하고 그래프와 같은 구조로 서로 연결하는 클래스가 있습니다. 가장 좋은 방법은 다음과 같습니다.

  1. 하나의 기능으로 노드를 초기화하고 연결하는 기능 유지
  2. 두 가지 다른 기능으로 초기화 및 연결 기능을 사용하십시오 (이 기능은 개인용이지만 명심해야합니다).

방법 1 : (하나의 기능이 두 가지 일을한다는 점에서 나쁘지만 종속 기능을 그룹화하여 유지합니다. 노드를 먼저 초기화하지 않고 연결해서는 안됩니다.)

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
}

방법 2 : (자체 문서화라는 점에서 더 좋은 점은 BUT connectNodes ()는 setupNodes () 전에 호출해서는 안되므로 클래스 내부 작업을하는 사람은이 순서를 알아야합니다.)

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
}

어떤 생각이라도 들었습니다.



최종 개체를 만드는 데만 사용할 수있는 중간 개체를 정의하여이 문제를 해결하는 한 가지 방법입니다. 항상 올바른 솔루션은 아니지만 인터페이스 사용자가 어떤 방식으로 중간 상태를 조작해야하는 경우에 유용합니다.
Joel Cornett

답변:


23

다루고있는 문제를 시간 결합 이라고합니다

이 코드가 얼마나 이해 가능한지 걱정할 권리가 있습니다.

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

나는 거기에서 무슨 일이 일어나고 있는지 추측 할 수 있지만 이것이 다른 일이 좀 더 명확하게되는지 말해줍니다.

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

이것은 인스턴스 변수 수정에 덜 연결되어 있다는 이점이 있지만 읽을 수있는 것이 가장 중요합니다.

이는 connectNodes()노드에 대한 종속성을 명시 적으로 만듭니다.


1
링크 주셔서 감사합니다. 내 함수는 비공개이며 Swift의 생성자 init ()에서 호출했기 때문에 내 코드가 링크 한 예제만큼 나쁘지 않다고 생각합니다 (외부 클라이언트가 인스턴스를 인스턴스화하는 것은 불가능합니다) null 인스턴스 변수), 비슷한 냄새가납니다.
mcfroob

1
추가 한 코드는 더 읽기 쉬우므로 해당 스타일로 리팩터링하겠습니다.
mcfroob

10

두 가지 이유로 인해 별도의 기능 :

1.이 상황에서 개인 기능은 개인 기능입니다.

귀하의 init기능은 공개이며, 인터페이스, 행동, 그리고 반환 값은 당신이 보호하고 변화에 대해 걱정할 필요가 것입니다. 해당 메소드에서 기대하는 결과는 사용하는 구현에 관계없이 동일합니다.

나머지 기능은 비공개 키워드 뒤에 숨겨져 있기 때문에 원하는대로 구현할 수 있습니다. 따라서 한 비트가 다른 비트가 먼저 호출되는 것에 의존하더라도 멋지고 모듈화 될 수 있습니다.

2. 노드를 서로 연결하는 것은 개인 기능이 아닐 수 있습니다

어떤 시점에서 다른 노드를 배열에 추가하려면 어떻게해야합니까? 지금 가지고있는 설정을 삭제하고 완전히 다시 초기화합니까? 아니면 기존 어레이에 노드를 추가 한 다음 connectNodes다시 실행 합니까?

아마도 connectNodes아직 생성되지 않은 노드의 배열 (예외를 던져? 당신이 당신의 상황에 의미가 무엇인지를 결정해야? 빈 집합을 반환) 경우 제정신 응답을 할 수 있습니다.


나는 1과 같은 방식으로 생각하고 있었고 노드가 초기화되지 않은 경우 예외 또는 무언가를 던질 수 있지만 특히 직관적이지는 않습니다. 답변 주셔서 감사합니다.
mcfroob

4

당신은 또한 (이러한 각각의 작업이 얼마나 복잡한 지에 따라) 이것이 다른 클래스를 분리하기에 좋은 솔기라는 것을 알 수 있습니다.

(Swift가 이런 식으로 작동하지만 의사 코드인지는 확실하지 않습니다.)

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
    }
}

이것은 클래스를 분리하기 위해 노드를 작성하고 수정하는 책임을 분리합니다.NodeGenerator 생성 / 검색 YourClass에만 관심이 있고 주어진 노드 연결에만 관심이 있습니다.


2

이것은 개인 메소드의 정확한 목적 일뿐 아니라 내부 기능을 사용할 수있는 기능을 제공합니다.

내부 메소드는 단일 호출 사이트 만있는 함수에 적합하지만 별도의 개인 함수 인 것을 정당화하지 않는 것 같습니다.

예를 들어, 전제 조건을 점검하고 일부 매개 변수를 설정하며 작업을 수행하는 개인 재귀 함수에 위임하는 공용 재귀 "항목"기능을 갖는 것이 매우 일반적입니다.

이 경우 어떻게 보일지에 대한 예는 다음과 같습니다.

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
    }
}

공유 상태를 변경하지 않고 반환 값과 매개 변수를 사용하여 데이터를 전달하는 방법에주의하십시오. 이를 통해 구현에 뛰어 들지 않고도 언뜻보기에 데이터 흐름이 훨씬 더 명확 해집니다.


0

선언 한 모든 기능에는 문서를 추가하고 프로그램의 다른 부분에서 사용할 수 있도록 일반화해야하는 부담이 따릅니다. 또한 파일의 다른 함수가 코드를 읽는 사람에게 파일을 사용하는 방법을 이해해야하는 부담이 있습니다.

그러나 프로그램의 다른 부분에서 사용되지 않으면 별도의 기능으로 노출시키지 않습니다.

언어가 지원하는 경우 중첩 된 함수를 사용하여 하나의 기능 만 수행 할 수 있습니다.

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

선언의 장소는 매우 중요하며, 위의 예에서 내부 함수가 외부 함수의 본문 내에서만 사용되어야한다는 추가 단서가 필요하지 않습니다.

개인 함수로 선언하더라도 전체 파일에서 여전히 볼 수 있다고 가정합니다. 따라서 주 함수의 선언에 가깝게 선언하고 외부 함수에서만 사용해야 함을 설명하는 문서를 추가해야합니다.

나는 당신이 엄격하게 하나 또는 다른 것을해야한다고 생각하지 않습니다. 가장 좋은 방법은 상황에 따라 다릅니다.

여러 기능으로 나누면 분명히 3 가지 기능이있는 이유와 기능이 서로 어떻게 작동하는지 이해하는 오버 헤드가 추가되지만 논리가 복잡한 경우이 추가 된 오버 헤드는 복잡한 논리를 분해하여 도입 된 단순성보다 훨씬 적을 수 있습니다 더 간단한 부분으로.


재미있는 옵션. 당신이 말했듯이, 왜 함수가 이렇게 선언되었는지에 대해서는 약간 수수께끼 일지 모르지만 함수 의존성을 잘 유지합니다.
mcfroob

이 질문의 일부 불확실성에 답하기 위해 : 1) 예, Swift는 내부 기능을 지원합니다. 그리고 2) "개인"의 두 가지 수준이 있습니다. private둘러싸는 유형 (struct / class / enum) 내에서만 fileprivate액세스를 허용 하는 반면 전체 파일을 통한 액세스를 허용합니다
Alexander-Reinstate Monica
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.