레이블이 캡슐화인 게시물을 표시합니다. 모든 게시물 표시
레이블이 캡슐화인 게시물을 표시합니다. 모든 게시물 표시

2016년 11월 15일 화요일

C++ : Class Capsulation

가끔씩 따로 관리하던 두 가지를 한꺼번에 관리해야 할 경우가 생길 수 있습니다.

이를테면 다음과 같은 애완동물호텔을 만들었는데...

class Pet : public Animal
{
public :
    virtual void Feed() = 0;
    virtual void Bath();                  // Pet bathing
};

class Dog : public Pet
{
public :
    bool Feed() override;        // Dog feed
};

class Cat : public Pet
{
public :
    bool Feed() override;        // Cat feed
}


class Hotel
{
private :
    std::list<Pet *> m_List;
public :
    ....
    void Care()
    {
        for each(Pet *pet in m_List)
        {
            pet->Feed();
            pet->Bath();
        }
    }
};

그런데 갑자기 주인이 자동차나 오토바이 보관까지 하겠다고 나선다면 어떨까요? 더구나 자동차나 오토바이는

class Car : public Machine
{
   virtual void Fual() = 0;
   void Wash();            // car washing
};

class MotorCycle : public Car
{
   void Fual() override;   // fual Gasolin
};

class Sedan : public Car
{
   void Fual() override;   // fual Gasolin
};

class Jeep : public Car
{
   void Fual() override;   // fual Desel
};

class F1Formular : public Car
{
   void Fual() override;   // fual High grade Gasolin
};

와 같이 전혀 관계없는 클래스라면 더욱 난감해집니다.

이 경우 이런 클래스들을 감싸는 클래스를 만들면 비교적 손쉽게 됩니다.

class GuestBase
{
public :
    virtual void Feed() = 0;
    virtual void Bath() = 0;
    virtual void Fuel() = 0;
};

class GuestPet : GuestBase
{
private :
    Pet *m_Pet;
public :
    virtual void Feed()
    {
        m_Pet->Feed();
    }
    virtual void Bath()
    {
        m_Pet->Bath();
    }
    virtual void Fuel()
    {
        // Do nothing
    }
};

class GuestMachine : GuestBase
{
private :
    Machine *m_Machine;
public :
    virtual void Feed()
    {
        // Do nothing
    }
    virtual void Bath()
    {
        m_Machine->Wash();
    }
    virtual void Fuel()
    {
        m_Machine->Fuel();
    }
};



class Hotel
{
private :
    std::list<GuestBase *> m_List;
public :
    ....
    void Care()
    {
        for each(Guest *guest in m_List)
        {
            guest->Feed();
            guest->Bath();
            guest->Fuel();
        }
    }
};

2016년 11월 8일 화요일

OOP 추상화(Abstraciton)

OOP의 특징 중 하나가 추상화(Abstraciton)입니다. 그런데 이 추상화가 뭘까요? 피카소의 그림일까요?

'추상화'란 것은, 어떤 기능을 가진 객체를 구현할때, 객체 외부에서는 그 기능의 정확한 묘사를 하지 않는 것입니다. 그 대신 그 기능의 '추상적 행위'를 묘사해야 합니다.

이를테면 다음과 같은 잠수함을 구현한다고 해 봅시다.

class Submarine
{
    private int x;    // x좌표
    private int y;    // y좌표
    private int depth;// 깊이

    public int GetX() { return x; }
    public int SetX(int x) { this.x = x; }
    ...........
}

void Function(Submarine ship)
{
    // 북쪽으로 이동
    ship.SetY(ship.GetY() - 1);


    // 수면으로 이동
    if(ship.GetDepth() > 0)
        ship.SetDepth(ship.GetDepth() - 1);
}

이런 식으로 해도 동작은 하겠죠. 하지만 이런 구현을 한다면 구태여 OOP 프로그램을 사용할 필요가 없습니다. 그냥 C로도 구현되는 수준이거든요. 오히려 C로 하면 저런 잡다한 멤버함수를 만들 필요도 없이 쉽게 끝납니다(하지만 그냥 C 프로그램에서도 이런 식의 프로그램은 좋지 않습니다*).

이것을 제대로 추상화를 구현한다면 이런 식이 되어야 합니다.

class Submarine
{
    private int x;    // x좌표
    private int y;    // y좌표
    private int depth;// 깊이

    public void ToEast() { ++x; }
    public void ToWest() { --x; }
    public void ToSouth() { ++y; }
    public void ToNorth() { --y; }

    public void Float()
    {
        if(depth > 0)
           --depth;
    }

    public void Sink() { ++depth; }
}

void Function(Submarine ship)
{
    // 북쪽으로 이동
    ship.ToNorth();

    // 수면으로 이동
    ship.Float();
}


이런 식으로 Submarine 밖에서는 Submarine의 멤버변수 자체를 생각하지 않고 코딩하는 것이 '추상화'입니다. x니 y니 따위를 생각하지 않고 오로지 '북쪽으로 이동'만 코딩하는 것이죠. '구체화'는 Submarine.ToNorth() 안에서 하고 말입니다.
캡슐화(Capsulation)는 멤버변수에 직접 접근이 힘들 뿐, 실제 멤버변수가 뭐가 있는지 알 수는 있다는 점에서, 추상화는 캡슐화보다 한단계 더 진행한 개념이라고 볼 수 있죠.

만약

class Ocean
{
    ......

    public bool IsReef(int x, int y)    // 암초가 있는가
    {
        .....
    }
}

class Submarine
{
    private int x;    // x좌표
    private int y;    // y좌표
    private int depth;// 깊이

    public int GetX() { return x; }
    public int SetX(int x) { this.x = x; }
    ...........
}

void Function(Ocean ocean, Submarine ship)
{
    // 북쪽으로 이동
    ship.SetY(ship.GetY() - 1);

    // 암초에 걸렸는지 체크
    if(ocean.IsReef(ship.GetX(), ship.GetY())
        ship.Destroyed();
}

이런 것은 '캡슐화'는 되었지만(x, y변수가 '캡슐화되었죠) '추상화'가 된 코드는 아닙니다. '추상화'를 시킨다면

class Ocean
{
    ......

    public bool IsReef(int x, int y)    // 암초가 있는가
    {
        .....
    }
}

class Submarine
{
    private int x;    // x좌표
    private int y;    // y좌표
    private int depth;// 깊이

    public void ToEast() { ++x; }
    public void ToWest() { --x; }
    public void ToSouth() { ++y; }
    public void ToNorth() { --y; }

    public bool IsSafe(Ocean ocean)
    {
        if(ocean.IsReef(x, y))  // 암초에 걸렸으면
            return false;
        // 다른 상황 체크

        // 모두 통과했으면 안전
        return true;
    }
    ...........
}

void Function(Ocean ocean, Submarine ship)
{
    // 북쪽으로 이동
    ship.ToNorth();

    // 배가 안전한지 체크
    if(ship.isSafe(ocean))
        ship.Destroyed();
}


가 되어야 합니다.


* 위에서 저런 식의 코딩은 C에서도 좋지 않다고 했는데, C에서의 코딩은 다음과 같이 하는 것이 좋습니다.

struct Ocean
{
    ......
}

struct Submarine
{
    int x;    // x좌표
    int y;    // y좌표
    int depth;// 깊이
}

void ToEast(Submarine ship) { ++ship.x; }
void ToWest(Submarine ship) { --ship.x; }
void ToSouth(Submarine ship) { ++ship.y; }
void ToNorth(Submarine ship) { --ship.y; }

bool IsSafe(Ocean ocean, Submarine ship)
{
    if(ocean.IsReef(ship.x, ship.y))  // 암초에 걸렸으면
        return false;
    // 다른 상황 체크

    // 모두 통과했으면 안전
    return true;
}

void Function(Ocean ocean, Submarine ship)
{
    // 북쪽으로 이동
    ToNorth(ship);

    // 배가 안전한지 체크
    if(isSafe(ocean, ship))
        Destroyed(ship);
}