본문 바로가기
까망 동네/클린 코드

[클린코드] 객체와 자료구조

by 까망 하르방 2022. 7. 19.
반응형

자료 구조

자료를 그대로 공개하며 별다른 함수 제공 X

class Point {
public:
    double x;
    double y;
};

 

내부 구조가 노출되어,

직접적으로 좌표값을 읽고 설정할 수 있다.

 

 

직접적인 접근을 제한하기 위해

비공개 private로 선언하고

getter와 setter 함수를 제공한다

class Point {
private:
    double x, y;
public:
    double getX() {return x;}
    double getY() {return y;}
}

 

함수라는 계층을 넣었다고 완전히 감춰지지 않는다.

내부 구조를 노출하는 구조에 해당된다.

 

객체

추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개

class Point {
public:
    virtual double getX() = 0;
    virtual double getY() = 0;
    virtual void setCartesian(double x, double y) = 0;
    virtual double getR() = 0;
    virtual double getTheta() = 0;
    virtual void setPolar(double r, double theta) = 0;
};

 

어떤 좌표계를 사용하는지 내부를 정확히 알 수 없다

ex) (X, Y)를 사용하는지 (r, θ) 를 사용하는지

 

 

실제 구현을 모른 채 추상 클래스를 제공받아

자료를 조작할 수 있어야 진정한 의미의 클래스

• 자료 구조: 자료를 그대로 공개하며 별다른 함수는 제공 X

• 객체: 추상화 뒤로 자료를 숨긴 채 자료를 다루는 함수만 공개

* 두 개념은 사실상 정반대


 

자료구조 기반 절차적인 코드

struct Shape {
public:
    virtual ~Shape() { }
};

struct Point : public Shape {
};

struct Square : public Shape {
    Point topLeft;
    double side;
};

struct Rectangle : public Shape {
    Point topLeft;
    double height;
    double width;
};

struct Circle : public Shape {
    Point center;
    double radius;
};

#define PI 3.141592653589793

class Geometry {
public:
    double area(Shape* pShape) {
        if (nullptr != dynamic_cast <Square*>(pShape)) {
            Square& s = *(Square*)pShape;
            return s.side * s.side;
        }
        else if (nullptr != dynamic_cast <Rectangle*>(pShape)) {
            Rectangle& r = *(Rectangle*)pShape;
            return r.height * r.width;
        }
        else if (nullptr != dynamic_cast <Circle*>(pShape)){
            Circle& c = *(Circle*)pShape;
            return PI * c.radius * c.radius;
        }
    }
};

 

도형(Square, Rectangle, Circle) 자료구조에

넓이(area) 구하는 함수를 직접 구현해주지 않고

Geometry 클래스르 통해서 각 도형의 넓이를 구해주었다.

 

 

Q) Geometry 클래스에 둘레 구하는 함수 perimeter() 추가한다면?

#include <iostream>

using namespace std;

struct Shape {
public:
    virtual ~Shape() {}
};

struct Point : public Shape {
};

struct Square : public Shape {
    Point topLeft;
    double side;
};

struct Rectangle : public Shape {
    Point topLeft;
    double height;
    double width;
};

struct Circle : public Shape {
    Point center;
    double radius;
};

#define PI 3.141592653589793

class Geometry {
public:
    double area(Shape* pShape) {
        if (nullptr != dynamic_cast<Square*>(pShape)) {
            Square& s = *(Square*)pShape;
            return s.side * s.side;
        }
        else if (nullptr != dynamic_cast<Rectangle*>(pShape)) {
            Rectangle& r = *(Rectangle*)pShape;
            return r.height * r.width;
        }
        else if (nullptr != dynamic_cast<Circle*>(pShape)) {
            Circle& c = *(Circle*)pShape;
            return PI * c.radius * c.radius;
        }
    }
    
    double perimeter(Shape* pShape) {
        if (nullptr != dynamic_cast<Square*>(pShape)) {
            Square& s = *(Square*)pShape;
            return (s.side + s.side) * 2;
        }
        else if (nullptr != dynamic_cast<Rectangle*>(pShape)) {
            Rectangle& r = *(Rectangle*)pShape;
            return (r.height + r.width) * 2;
        }
        else if (nullptr != dynamic_cast<Circle*>(pShape)) {
            Circle& c = *(Circle*)pShape;
            return 2 * PI * c.radius;
        }
    }
};

 

장점 

도형 클래스(Square, Rectangle, Circle)는 아무 영향도 받지 않는다 !

(도형 클래스에 의존하는 다른 클래스도 마찬가지)

새로운 함수가 필요하다면 Geometry에만 추가하면 된다.

 

 

단점

새로운 도형을 추가한다면

Geometry 클래스에서 

해당 도형을 처리하는 모든 함수를 점검해야 한다.


 

객체 지향 기법

struct Shape {
public:
    virtual ~Shape() { }
    virtual double area() const = 0;
};

struct Point : public Shape {
public:
    double area() const { return 1;}
};

#define PI 3.141592653589793

struct Square : public Shape {
private:
    Point topLeft;
    double side;
public:
    double area() const {
        return side * side;
    }
};

struct Rectangle : public Shape {
private:
    Point topLeft;
    double height;
    double width;
public:
    double area() const {
        return height * width;
    }
};

struct Circle : public Shape {
private:
    Point center;
    double radius;
public:
    double area() const {
        PI* radius* radius;
    }
};

class Geometry {
public:
    double area(const Shape& shape) {
        return shape.area();
    }
};

 

각 도형 클래스마다 넓이 구하는

area() 함수가 구현되어 있다.

 

Q) Geometry 클래스에 둘레 구하는 함수 perimeter() 추가한다면?

#include <iostream>

using namespace std;

class Shape {
public:
    virtual ~Shape() {}
    virtual double area() const = 0;
    virtual double perimeter() const = 0; // 추가
};

class Point : public Shape {
public:
    double area() const { return 1;}
    virtual double perimeter() const { return 1; } // 추가
};

#define PI 3.141592653589793

class Square : public Shape {
private:
    Point topLeft;
    double side;
public:
    double area() const {
        return side * side;
    }
    virtual double perimeter() const { // 추가
        return (side + side) * 2;
    }
};

class Rectangle : public Shape {
private:
    Point topLeft;
    double height;
    double width;
public:
    double area() const {
        return height * width;
    }
    double perimeter() const { // 추가
        return (height + width) * 2;
    }
};

struct Circle : public Shape {
private:
    Point center;
    double radius;
public:
    double area() const {
        return PI * radius * radius;
    }
    double perimeter() const { // 추가
        return 2 * PI * radius;
    }
};

class Geometry {
public:
    double area(const Shape& shape) {
        return shape.area();
    }

    double perimeter(const Shape& shape) { // 추가
        return shape.perimeter();
    }
};

 

장점

새로운 도형 클래스를 추가하기 쉽다.

ex) Shape를 구현하는 도형을 만들면 된다.

 

 

단점

둘레구하기 등 새로우 함수 추가시

각 도형 클래스마다 구현해야 하기 때문에 어렵다.


 

요약

• 절차지향: 기존 자료 구조를 변경하지 않으면서 새로운 함수를 추가하기 쉽다.

    ex) 새로운 도형 추가시 다루는 함수 점검 필요

• 객체지향: 기존 함수를 변경하지 않으면서 새로운 클래스 추가하기 쉽다.

    ex) 기존 함수를 만족하는 클래스를 만들면 된다.

* 두 구조 차이를 알아보기 위해 새로운 함수를 추가하였지만

   새로운 도형을 추가해서 차이를 느낄 수도 있다.

 

Q) 그럼 우리는 무엇을 선택해야 할까?

A) 개발자는 직면한 문제에 최적인 해결책을 선택 해야한다

 

<자료구조 기반 절차 지향>

별다른 동작 없이 자료를 노출한다.

그래서 기존 자료 구조에 새 동작을 추가하기는 쉽다.

하지만 기존 함수에 새로운 자료 구조를 추가하기 어렵다

→ 새로운 동작(함수)가 요구되는 경우

 

 

<객체 지향>

동작을 공개하고 자료를 숨긴다.

그래서 기존 동작을 변경하지 않으면서 새 객체 타입을 추가하기 쉽다

하지만 기존 객체에 새로운 동작을 추가하기는 어렵다

→ 새로운 자료 타입이 요구되는 경우

 

 

📌 클린 코드(Clean Code)란?

 

클린 코드(Clean Code)란?

💻 클린 코드 (Clean Code)? • 프로그래밍을 모르는 사람도 한눈에 읽히는 코드 (가독성) • 다른 사람이 수정하기 쉬운 코드 • 한 가지 일에 집중하는 코드 • 중복이 적은 코드 • 테스트가

zoosso.tistory.com

반응형

'까망 동네 > 클린 코드' 카테고리의 다른 글

[클린코드] 형식 (Format)  (0) 2022.07.18
[클린코드] 함수 Function  (0) 2022.07.18
[클린코드] 주석 Comment  (0) 2022.07.18
[클린코드] 의미 있는 이름  (0) 2022.07.17
클린 코드(Clean Code)란?  (0) 2022.07.17

댓글