코드 냄새 125 - 'IS-A' 관계

18638 단어
우리는 상속이 'is-a' 관계를 나타낸다는 것을 학교에서 배웠습니다. 그렇지 않습니다.

TL:DR; Think about protocol and behavior, forget inheritance



문제


  • 불량 모델
  • 예기치 않은 동작
  • 하위 클래스 재정의
  • Liskov 대체 원칙 위반

  • 솔루션


  • 행동의 관점에서 생각하는 행동
  • 상속보다 합성을 선호함
  • 항상 'behaves-as-a' 관계를 따르는 하위 ​​분류

  • 문맥



    IS-A relation은 데이터 세계에서 나옵니다.

    우리는 구조화된 디자인과 데이터 모델링으로 ERDs 배웠습니다.

    이제 우리는 행동의 관점에서 생각해야 합니다.

    행동은 필수적이며 데이터는 .

    샘플 코드



    잘못된




    // If you made Square derive from Rectangle, then a Square should be usable anywhere you expect a rectangle
    
    #include <iostream>
    
    Rectangle::Rectangle(const unsigned width, const unsigned height):
        m_width(width),
        m_height(height)
    {
    }
    
    unsigned Rectangle::getWidth() const
    {
        return m_width;
    }
    
    void Rectangle::setWidth(const unsigned width)
    {
      /*Width and Height are independant*/
        m_width = width;
    }
    
    unsigned Rectangle::getHeight() const
    {
        return m_height;
    }
    
    void Rectangle::setHeight(const unsigned height)
    {
        m_height = height;
    }
    
    unsigned Rectangle::area() const
    {
      /*Valid for both Rectangles and Squares*/
        return m_height * m_width;
    }
    
    Square::Square(const unsigned size)
        : Rectangle(size, size)
    {
    }
    
    // OK for squares, bad for rectangles
    // Protocol is bad, width and height are not relevant on squares
    void Square::setWidth(const unsigned size)
    {
        m_height = size;
        m_width = size;
    }
    
    // OK for squares, bad for rectangles
    // Protocol is bad, width and height are not relevant on squares
    void Square::setHeight(const unsigned size)
    {
        m_height = size;
        m_width = size;
    }
    
    void process(Rectangle& r)
    {
        unsigned h = 10;
        auto w = r.getWidth();
        r.setHeight(h);
    
        std::cout << "Expected area: " << (w*h) << ", got " << r.area() << "\n";
      //area is not well defined in squares
      //every square IS-A rectangle, but does not behave-like a rectangle
    }
    
    int main()
    {
        Rectangle rectangle{3,4};
        Square square{5};
        process(rectangle);
        process(square);
    }
    

    오른쪽



    // If you made Square derive from Rectangle, 
    //then a Square should be usable anywhere you expect a rectangle
    
    #include <iostream>
    
    Rectangle::Rectangle(const unsigned width, const unsigned height):
        m_width(width),
        m_height(height)
    {
    }
    
    void Rectangle:changeWidth(const unsigned width)
    {
      /*Width and Height are independant*/
        m_width = width;
        // We should discuss if it is good to mutate 
        // and not create a new Figure
    }
    
    void Rectangle::changeHeight(const unsigned height)
    {
        m_height = height;
    }
    
    unsigned Rectangle::area() const
    { 
        return m_height * m_width;
    }
    
    //No inheritance
    Square::Square(const unsigned size):
        m_size(size)
    {
    }
    
    unsigned Square::area() const
    { 
        return m_size * m_size;
    }
    
    void Square::changeSize(const unsigned size)
    {
        m_size = size; 
    }
    
    void testRectangleChange(Rectangle& r)
    {
        unsigned h = 10;
        auto w = r.getWidth();
        r.setHeight(h);
    
        std::cout << "Expected area: " << (w*h) << ", got " << r.area() << "\n";
      //area is not well defined in squares
      //every square IS-A rectangle, but does not behave-like a rectangle
    }
    
    int main()
    {
        Rectangle rectangle{3,4};
        Square square{5};
        testRectangleChange(rectangle);
        testRectangleChange(square);
    }
    

    발각



    [X] 수동

    이것은 시맨틱 냄새입니다.

    태그


  • 상속

  • 결론



    실수 IS-A 복소수(수학에 따름).

    정수 IS-A 실수(수학에 따름).

    실수는 복소수처럼 행동하지 않습니다.

    우리는 real.setImaginaryPart()를 수행할 수 없으므로 우리에 따르면 Complex가 아닙니다.

    처지
















    더 많은 정보


  • Circle/Ellipse Problem
  • Subtyping

  • 학점



    사진 제공: Joshua Rondeau on Unsplash


    DRY - Don't Repeat Yourself - Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.



    앤디 헌트






    이 기사는 CodeSmell 시리즈의 일부입니다.


    좋은 웹페이지 즐겨찾기