복붙노트

[SPRING] 인터페이스 레벨에서 두 클래스를 분리하면 무엇을 의미합니까?

SPRING

인터페이스 레벨에서 두 클래스를 분리하면 무엇을 의미합니까?

우리는 클래스 A가 패키지 A에 있고 클래스 B가 패키지 B에 있다고 가정 해 보겠습니다. 클래스 A의 객체가 클래스 B에 대한 참조를 가지고 있다면, 두 클래스는 그것들 사이의 결합을 가지고 있다고 말해진다.

연결을 처리하려면 패키지 B의 클래스에 의해 구현되는 패키지 A의 인터페이스를 정의하는 것이 좋습니다. 그런 다음 클래스 A의 객체는 패키지 A의 인터페이스를 참조 할 수 있습니다. 이것은 종종 "종속성의 반전 (inversion of dependency)"의 한 예입니다.

이것은 "인터페이스 수준에서 두 개의 클래스 분리"의 예입니다. 그렇다면 두 클래스가 결합되었을 때 클래스 간의 결합을 제거하고 동일한 기능을 유지하는 방법은 무엇입니까?

해결법

  1. ==============================

    1.가상의 예를 만들어 보겠습니다.

    가상의 예를 만들어 보겠습니다.

    패키지 패키지 A의 클래스 A :

    package packageA;
    
    import packageB.B;
    
    public class A {
        private B myB;
    
        public A() {
            this.myB = new B();
        }
    
        public void doSomethingThatUsesB() {
            System.out.println("Doing things with myB");
            this.myB.doSomething();
        }
    }
    

    패키지 B의 클래스 B :

    package packageB;
    
    public class B {
        public void doSomething() {
            System.out.println("B did something.");
        }
    }
    

    보시다시피, A는 B에 의존합니다. B가 없으면 A는 사용할 수 없습니다. 그러나 미래에 B를 BetterB로 대체하기를 원하면 어떻게해야할까요? 이를 위해 packageA 내에 Interface Inter를 만듭니다.

    package packageA;
    
    public interface Inter {
        public void doSomething();
    }
    

    이 인터페이스를 사용하기 위해 packageA.Inter; B가 B에서 Inter를 구현하고 A 내의 B의 모든 발생을 Inter로 대체하도록한다. 결과는 A의 수정 된 버전입니다.

    package packageA;
    
    public class A {
        private Inter myInter;
    
        public A() {
            this.myInter = ???; // What to do here?
        }
    
        public void doSomethingThatUsesInter() {
            System.out.println("Doing things with myInter");
            this.myInter.doSomething();
        }
    }
    

    이 시점에서 우리는 이미 A에서 B 로의 의존성이 없어 졌음을 알 수 있습니다 : import packageB.B; 더 이상 필요하지 않습니다. 인터페이스의 인스턴스를 인스턴스화 할 수 없습니다. 그러나 컨트롤의 반전이 구조에옵니다 : Interwihin A의 생성자 유형을 인스턴스화하는 대신 생성자는 Inter를 매개 변수로 구현하는 무언가를 요구합니다.

    package packageA;
    
    public class A {
        private Inter myInter;
    
        public A(Inter myInter) {
            this.myInter = myInter;
        }
    
        public void doSomethingThatUsesInter() {
            System.out.println("Doing things with myInter");
            this.myInter.doSomething();
        }
    }
    

    이 접근 방식을 통해 이제 Inter within A의 구현을 자유롭게 변경할 수 있습니다. BetterB라는 새로운 클래스를 작성한다고 가정 해 보겠습니다.

    package packageB;
    
    import packageA.Inter;
    
    public class BetterB implements Inter {
        @Override
        public void doSomething() {
            System.out.println("BetterB did something.");
        }
    }
    

    이제 서로 다른 Inter-implementation을 사용하여 As를 인스턴스화 할 수 있습니다.

    Inter b = new B();
    A aWithB = new A(b);
    aWithB.doSomethingThatUsesInter();
    
    Inter betterB = new BetterB();
    A aWithBetterB = new A(betterB);
    aWithBetterB.doSomethingThatUsesInter();
    

    그리고 A에서 아무 것도 변경할 필요가 없었습니다. 이제 코드가 분리되어 Inter의 계약이 충족되는 한 Inter의 의사 결정을 변경할 수 있습니다. 특히, 우리는 미래에 작성되어 Inter를 구현하는 코드를 지원할 수 있습니다.

    부록

    2 년 전이 답변을 썼습니다. 전반적으로 대답에 만족하면서, 나는 항상 무언가가 빠져 있다고 생각했고 나는 그것이 그것이 무엇인지를 마침내 알 것 같았다. 다음은 답을 이해하는 데 반드시 필요한 것은 아니지만, 독자에 대한 관심을 자극 할뿐만 아니라 추가자가 학습을위한 자원을 제공하기위한 것입니다.

    문헌에서이 접근법은 인터페이스 분리 원칙으로 알려져 있으며 SOLID 원칙에 속합니다. YouTube에서 삼촌 Bob과 좋은 대화가 있습니다 (재미있는 비트는 약 15 분 정도입니다). 다형성과 인터페이스를 사용하여 컴파일 시간 의존성을 컨트롤의 흐름에 맞출 수있는 방법을 보여줍니다. (시청자의 재량권이 권장됩니다. Java에 대해서는 약간 놀랐습니다). 이 대가로, 상위 레벨 구현은 인터페이스를 통해 분리 될 때 하위 구현에 대해 알 필요가 없다는 것을 의미합니다. 따라서 위에서 보았 듯이 낮은 수준을 자유롭게 바꿀 수 있습니다.

  2. ==============================

    2.B의 기능이 어떤 데이터베이스에 로그를 쓰는 것이라고 상상해보십시오. 클래스 B는 클래스 DB의 기능에 의존하며 다른 클래스에 대한 로깅 기능을위한 인터페이스를 제공합니다.

    B의 기능이 어떤 데이터베이스에 로그를 쓰는 것이라고 상상해보십시오. 클래스 B는 클래스 DB의 기능에 의존하며 다른 클래스에 대한 로깅 기능을위한 인터페이스를 제공합니다.

    클래스 A는 B의 로깅 기능을 필요로하지만 로그가 기록되는 곳은 상관하지 않습니다. 그것은 DB를 신경 쓰지 않지만 B에 의존하기 때문에 DB에 달려있다. 이것은별로 바람직하지 않습니다.

    그래서 당신이 할 수있는 것은 클래스 B를 두 클래스로 분리하는 것입니다 : 로깅 기능을 기술하는 (그리고 DB에 의존하지 않는) 추상 클래스 L과 DB에 따른 구현.

    그러면 A는 B에서 클래스 A를 분리 할 수 ​​있습니다. 왜냐하면 A는 L에만 의존하기 때문입니다. B는 L에 의존하기 때문에 B가 L에서 제공하는 기능을 제공하기 때문에 의존성 반전이라고합니다.

    이제는 A가 단순한 L에만 의존하기 때문에 DB에 의존하지 않고 다른 로깅 메커니즘과 함께 쉽게 사용할 수 있습니다. 예 : L에서 정의 된 인터페이스를 구현하여 간단한 콘솔 기반 로거를 만들 수 있습니다.

    그러나 이제 A는 B에 의존하지 않고 (소스에서) 런타임시 추상 인터페이스 L에서만 L (예 : B)의 특정 구현을 사용하도록 설정되어야합니다. 따라서 런타임 중에 B (또는 다른 것)를 사용하도록 A에게 지시하는 다른 사람이 있어야합니다. A를 B를 사용하기로 결정하기 전에 B 이외의 다른 컨테이너 (예 : 컨테이너)가 B에게 런타임 중에 B를 사용하도록 지시하기 때문에이를 제어 반전이라고합니다.

  3. ==============================

    3.사용자가 설명하는 상황은 클래스 A가 클래스 B의 특정 구현에 대해 갖는 의존성을 제거하고이를 인터페이스로 대체합니다. 이제 클래스 A는 클래스 B 만 받아들이는 대신 인터페이스를 구현하는 유형의 모든 객체를 허용 할 수 있습니다. 클래스 B가 해당 인터페이스를 구현하도록 설계되었으므로 디자인은 동일한 기능을 유지합니다.

    사용자가 설명하는 상황은 클래스 A가 클래스 B의 특정 구현에 대해 갖는 의존성을 제거하고이를 인터페이스로 대체합니다. 이제 클래스 A는 클래스 B 만 받아들이는 대신 인터페이스를 구현하는 유형의 모든 객체를 허용 할 수 있습니다. 클래스 B가 해당 인터페이스를 구현하도록 설계되었으므로 디자인은 동일한 기능을 유지합니다.

  4. from https://stackoverflow.com/questions/30784215/what-does-decoupling-two-classes-at-the-interface-level-mean by cc-by-sa and MIT license