>

두 가지 수업이 있습니다 :

class A {
    final B b;
    A(final B b) {
    this.b = b;
    }
    void doIt() {
    b.doSomething();
    }
    void doSomething() {
    // TODO Auto-generated method stub
    }
}

class B {
    final A a;
    B(final A a) {
    this.a = a;
    }
    void doIt() {
    a.doSomething();
    }
    void doSomething() {
    // TODO Auto-generated method stub
    }
}

그 중 하나를 인스턴스화 할 수있는 방법이 없습니다.

세터를 사용할 수 있습니다 (예 :

class B {
    A a;
    B() {
    }
    void setA(final A a) {
    this.a = a;
    }
    void doIt() {
    a.doSomething();
    }
    void doSomething() {
    // TODO Auto-generated method stub
    }
}

하지만 여기 a 를 확인해야합니다   null   doIt()  이 경우를 처리합니다. 이것은 세상의 끝이 아니라 아마도

<강한>그것을 할 수있는 더 똑똑한 방법은?

아마도 이것은 일반적으로안티 패턴일 수도 있고 처음에는 아키텍처에 문제가 있습니까?

을 문제의 루트

엔티티를로드 할 데이터베이스가 있습니다. 로드 유형 사이에에 양방향 관계를 구축하기 위해이 캐시를 사용하면 내가 그들을 캐시합니다. A 의 여러 인스턴스를로드 할 때 필요하기 때문에 그들은 (이 경우) B 의 모든 동일한 인스턴스가 있어야합니다 . 따라서 B를 인스턴스화하고 재사용해야합니다. 그러나 B  이 모든 a 와의 관계도 따라서 주기적 종속성.

그래서 더 짧은<강한>순환 종속성 데이터베이스양방향 관계에 의해 발생된다. 가능한 한 피하는 데 사용하지만 여기에 설명 된 문제 외에도 bi-di 관계에 대한 '실제'문제는 없습니다. 실제로는 매우 자연 스럽습니다.

어쩌면 문제는관계형 데이터베이스에서 oop 세계로 bi-di-relationship을 올바르게 매핑하는 방법일까요?

보다 구체적인 예 :

bi-di 관계를 설정하기 위해 엔티티 ID가 동일 할 때 동일한 인스턴스를 갖도록 캐시에서 인스턴스를로드합니다.

interface Cache<T>
interface CacheA extends Cache<A>
interface CacheB extends Cache<B>
class CacheManager {
    final CacheA cacheA;
    final CacheB cacheB;
    CacheManager(final DatabaseAccess databaseAccess) {
    cachA = new CacheA();
    cachB = new CacheB();
    cacheA.setEntityLoader(id -> new SimpleAttachedA(id, databaseAccess, cacheB));
    cacheB.setEntityLoader(id -> new SimpleAttachedB(id, databaseAccess, cacheA));
    }
}

A 의 인스턴스   B 와의 관계인 경우이 캐시에 액세스합니다.  액세스됩니다. 다른 방법도 마찬가지입니다.

CacheA 라면 가장 좋을 것 같아  그리고 CacheB  나는 그냥 캐시를 만들고 A 의 모든 인스턴스에게 전달 할 수 있기 때문에, 같은 객체가 될 것입니다  그리고 B .

CacheA  그리고 CacheB  예전에는 똑 같았습니다. Cache . 내가 제네릭 클래스를 사용하여 중복 코드를 많이 제거 할 수 있도록, 사람들을 분리하기로 결정했습니다.

CacheManager   CacheA 를 구현할 수 없습니다  그리고 CacheB 둘 다 동일한 일반 인터페이스 Cache<T> 를 확장하는 경우  그러나 다른 유형의 T .

따라서 CacheManager  상속 대신 구성을 사용합니다. 내가 액세스해야 두 개의 캐시, 끝낼 그래서 서로가 A 의 바이 디의 관계를 실현하기  그리고 B .


  • 답변 # 1

    순환 종속성은 일반적으로 잘못된 디자인의 신호입니다. 순환 종속성을 막을 수없는 경우도 있지만 항상 다른 솔루션에 대해 생각해야합니다. 주기적 중복성이있는 경우 클래스 중 하나를 변경하고 다른 클래스도 변경해야 할 가능성이 있습니다. 사이클에 둘 이상의 클래스가 포함되어 있으면 많은 작업이 필요할 수 있습니다. 또한 언급 한 것과 같은 문제 중 하나를 인스턴스화하려면 다른 클래스가 필요합니다.

    클래스가 더미 클래스 인 것처럼 보이지만 실제로 조언을 해줄 수는 없지만 일반적인 요점은 없습니다.

    클래스는높은 응집력낮은 커플 링을 가져야합니다. 즉, 클래스는 가능한 한 적은 클래스에 의존해야하며 (낮은 커플 링) 모든 클래스는 하나의 기능을 수행해야하며 모든 기능 (높은 응집력)을 수행해야합니다.

    서로 의존하는 두 개의 클래스가있을 때, 이것은 기능의 일부가 A 클래스에 있기 때문에 일반적으로 낮은 응집력을 나타내는 신호입니다.  클래스 B 의 또 다른 부분 . 이 경우 두 클래스를 하나의 클래스로 병합하는 것이 좋습니다.

    반면에, 병합 된 클래스의 클래스 이름을 찾고 ThisAndThatDoer 와 같은 것을 찾으려고 할 때 , 당신은 두 개의 클래스로 나누어야합니다  그리고 ThisDoer 이는 응집력이 낮다는 표시입니다. 그런 다음 원래 클래스를 서로 다시 의존하면 새 클래스 ThatDoer 를 만들 수 있습니다.  두 클래스를 연결합니다. 그러나 이것은 빠르게신 클래스가 될 수 있으며 이는 또한 안티 패턴입니다. 따라서 조심해야합니다.

    따라서 나는 클래스 디자인에 대해 생각하고 적어도 한 방향으로 만연 성을 제거하는 방법을 찾는 것이 좋습니다. 이것이 귀하의 문제에 도움이되기를 바랍니다.

  • 답변 # 2

    이것은 실제로 반 패턴으로 간주되며 더 좋은 방법이 있습니다. 만약 Executor   A 필요  B는 A가 필요합니다.

    개념적으로 함께 속하는 기능은 두 가지 구성 요소 (A와 B)로 나뉩니다. 각 구성 요소는 기능의 일부만 구현하므로 다른 구성 요소는 누락 된 작업을 수행해야합니다.솔루션 :A와 B가 함께 속하는 부분을 식별하고 새로운 구성 요소 C로 옮깁니다. A와 B의 남은 부분은 C에 의존하고 A와 B 사이의 의존성을 제거하십시오.
  • A는 2 개의 개별 구성 요소로 나눌 수있는 2 개의 개별 기능 A1 및 A2를 포함한다. A1에 B가 제공하는 기능이 필요하고 B에 A1이 제공하는 기능이 필요한 경우 A와 B 사이의 순환 종속성이 발생합니다.해결 방법 :A를 두 개의 개별 구성 요소 A1과 A2로 나누고 A1을 B에 의존하게하고 B를 A2에 의존하게하십시오.

    앞서 언급했듯이 순환 종속성이 괜찮은 경우가 있으며 피할 경우 더 모호한 디자인이 될 수 있습니다. 그러나 종종 그렇지 않습니다. 순환 종속성을 피하기 위해 기능을 재구성해야합니다.

    특정 사례에 대한 자세한 정보를 제공하는 경우 (예 : A와 B가하는 일과 서로 의존하는 부분이있는 경우) 더 많은 팁을 줄 수 있습니다.

  • 답변 # 3

    B

    1) 두 클래스가 다른 클래스의 모든 멤버에 액세스 할 필요가없는 경우 두 콘크리트 클래스 사이의 양방향 커플 링은 가능한 한 피해야합니다.
    일반적으로 필요 이상의 커플 링을 생성하십시오.

    2) 양방향 커플 링은 필요한 경우 문제가되지 않지만 오브젝트 구성에 주기적 종속성이있는 경우에는 문제가됩니다.
    깨지 않고 해결할 수 없기 때문에 악취가납니다.

    세터가있는 솔루션은 실제로 해결 방법입니다.
    정말로 피하고 두 클래스에서 생성자를 유지하려면 디자인을 변경하면 몇 가지 해결책이 있습니다.

    예를 들어 두 매개 변수 (A 또는 B 클래스) 중 하나를

    Or maybe this is even an anti pattern in general and something is wrong with the architecture in the first place?

    를 작성하기위한 모든 데이터를 포함하는 클래스로 대체 할 수 있습니다.  또는 A  인스턴스.

    B 의 생성을 추상화하고 싶다고 가정하십시오.  :

    A

    B
    
    

    B

    public A(ContextForB contextForB) {
        // init A data
          ...
        // create B from context and set the B dependency
        b = new B(contextForB, this);
    }
    
    

    public B(ContextForB contextForB, A a) { //.. create B from the context ... // set the A dependency this.a = a; }  무엇이든 될 수 있습니다.
    와이즈 비즈 , ContextForB , ResultSet , Query  등 ...

    다음과 같은 방법으로 사용할 수 있습니다 :

    Iterator
    
    

  • 답변 # 4

    인터페이스 만들기

    Supplier
    
    

    그런 다음 A와 B에서 사용하십시오 :

    // instantiate both from A
    A a = new A(new ContextForB(...));  
    // OR instantiate both from B
    ContextForB contextForB = new ContextForB(...);
    B b = new B(contextForB, new A(contextForB));
    
    

    public interface Doable {
          void doSomething();
    }
    
    

    여기서 주기적 의존성을 피할 것이다.

    class A implements Doable { final Doable b; A(final Doable b) { this.b = b; } void doIt() { b.doSomething(); } @Override void doSomething() { // TODO Auto-generated method stub } }

  • 이전 java - sqlsyntaxerrorexception - ora-02287 : 시퀀스 번호는 여기서는 사용할 수 없습니다
  • 다음 php - 필드가있는 SQL 조인 광고는 필드를 열로 표시합니다