Corgi Dog Bark

ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인 패턴] SOLID 의 Interface Segregation Principle
    알고리즘/디자인 패턴(Design Patterns) 2022. 12. 13. 20:03
    반응형

    디자인 패턴에 대한 정리 내용 중 SOLID 원칙의 네 번째 원칙인 Interface Segregation Principle 즉, 인터페이스 분리 원칙에 대하여 적어보겠습니다. SOLID 설계 원칙은 유지 보수가 쉽고 재사용 가능하도록 하고, 확장 가능한 개발을 위해 지켜지고 있습니다. 저의 네 번째 디자인 패턴 글에서는 C ++의 인터페이스 분리 원칙과 이를 사용하였을 때, 장점 등을 보겠습니다.

     

    이 글 시리즈에서는 총 SOLID의 5가지 원칙에 대해 살펴보겠습니다.

    1. SRP - Single Responsibility Principle
    2. OCP - Open/Closed Principle
    3. LSP - Liskov Substitution Principle
    4. ISP - Interface Segregation Principle
    5. DIP - Dependency Inversion Principle

     


     

    인터페이스 분리 원칙 정리

    인터페이스 모든 항목에 대한 구현을 강제하지 않고, 필요한 인터페이스만 구현할 수 있도록 한다.

    우선 인터페이스 분리 원칙은 단일 책임 원칙과 매우 많은 관련이 있습니다. 인터페이스 분리 원칙이 의미하는 것은 노출된 방법을 사용하는 클라이언트가 전체 인터페이스를 모두 구현하지 않게끔 항상 추상화를 설계해야 한다는 것입니다. 고객에게 실제로 필요하지 않은 방법을 구현해야 하는 부담을 지우는 것을 인터페이스 분리 원칙의 핵심 설계 원칙이라 할 수 있겠습니다.

     

     


     

     

    인터페이스 분리 원칙 위반 예시

    struct Document;
    /*----- ABSTRACT CLASS -----*/
    struct IMachine {
        virtual void print(Document &doc) = 0;
        virtual void fax(Document &doc) = 0;
        virtual void scan(Document &doc) = 0;
    };
    
    struct MultiFunctionPrinter : IMachine {     
        void print(Document &doc) override { }
        void fax(Document &doc) override { }
        void scan(Document &doc) override { }
    };
    
    struct Scanner : IMachine {                  
        void print(Document &doc) override { /* Override */ }
        void fax(Document &doc) override { /* Override */ }
        void scan(Document &doc) override {  
            // Do scanning ...
        }
    };

     위의 코드 예시를 보았을 때, MultiFunctionPrinter의 경우, print( ), fax( ), scan( ) 기능을 모두 활용하므로, Client 가 모두 구현을 해도 상관이 없습니다. 그러나 Scanner와 같은 경우, Scan의 기능만이 필요하지만, print( ), fax( ), scan( )의 3가지 기능을 모두 구현해야 하므로, 인터페이스 분리 원칙을 어겼다고 할 수 있겠습니다.

     

     


     

     

    인터페이스 분리 구현 방법

    앞서 살펴본 예제를 인터페이스 분리 원칙(ISP)을 지키도록 분리해보도록 하겠습니다. 우선 IMachine 이 가지고 있는 하나의 인터페이스를 Print / Scan 기능으로 분리해보도록 하겠습니다.

     

    인터페이스 분리

    /* ----- Interface ----- */
    struct IPrinter {
        virtual void print(Document &doc) = 0;
    };
    struct IScanner {
        virtual void scan(Document &doc) = 0;
    };

    위와 같이 Printer 와 Scanner로 기능을 분리시켜 주었습니다.

    그다음, IPrinter와 ISanner 가 가진 인터페이스를 구현하는 클래스를 만들어준 다음, IMachine에서 각 인터페이스를 조합하였습니다.

    struct Printer : IPrinter {
        void print(Document &doc) override;
    };
    struct Scanner : IScanner {
        void scan(Document &doc) override;
    };
    
    struct IMachine : IPrinter, IScanner { };
    
    struct Machine : IMachine {
        IPrinter&   m_printer;
        IScanner&   m_scanner;
        Machine(IPrinter &p, IScanner &s) : printer{p}, scanner{s} { }
        void print(Document &doc) override { printer.print(doc); }
        void scan(Document &doc) override { scanner.scan(doc); }
    };

    이렇게 함으로써, Printer 와 Scanner 클래스를 사용자가 원할 시, 인터페이스를 조합하여 만들 수 있게 되었으며, Print와 Scan의 기능을 분리해줌으로써, 서로의 결합도를 느슨하게 해 줄 수 있었습니다.

     

     


     

     

    인터페이스 분리 원칙의 장점

    1. 재사용성

    • 쓸모없는 인터페이스를 가지고 있는 클래스들 간의 결합은 커플링 현상이 발생하게 되어 재사용에 어려움이 따르게 됩니다. 따라서 인터페이스 설계자는 인터페이스 분리원칙을 통해, 최대한 클래스들이 분리될 수 있게끔 설계해야 합니다.

    2. 유지 보수 측면

    • 읽기 쉬운 인터페이스를 만들 수 있으며 ( 분리 하였기 때문에 무슨 기능을 하는지 좀 더 확실히 알 수 있습니다.)
    • 기능의 분리로 인한 변경이 용이합니다.

     


     

     

    결론

    인터페이스 분리원칙을 다시 한번 정리하자면, 한 덩어리의 복잡한 인터페이스를 목적에 따라 다르게 분리함으로써, 결합도를 최대한 느슨하게 하는 것을 원칙으로 하고 있음을 확인하였습니다. 만약 어떤 어플리케이션의 플러그인 모듈을 개발할 때 엄청나게 많은 수십 개의 함수를 빈 함수 또는 null 리턴으로 구현하고 있다면, 이는 인터페이스 분리 원칙(ISP)을 위반했다고 할 수 있겠습니다.

     

     

     

    반응형

    댓글

Designed by Tistory.