Post

About OOP Design

개인과제를 진행하면서 OOP 관련 내용을 정리 하다보니 결국 한쪽을 선택해야 되는 상황에 이렀다.
나는 과제도 중요 하다고는 생각하지만 기본BASIC이 더 중요하다 생각해 OOP를 이해하는데 신경을 많이 썻다.

🔗Post Link


S.O.L.I.D 원칙을 이해하면서

  • 단일 책임 원칙

  • 솔리드 원칙에서 가장 중요하게 이행해야될 부분이 있다.
  • 단일 책임 원칙인데, 클래스는 단 한가지에 책임만을 져야한다는 것이다.
  • 이 부분만 제대로 이행하더라도 나머지는 의존성을 줄이던지 천천히 해나가면 된다.
1
2
3
4
5
6
7
8
9
10
11
12
class PlayerCtrl
{
public:
	void PlayerMovement() { ... }
	void PlayerRotate() { ... }
	void PlayerInput() { ... }

private:
	Vector2 playerDirection;
	Vector2 playerRotation;
	// ...
};
  • 위 코드에 문제점은 해당 클래스는 플레이어의 이동, 회전, 입력과 같은 모든 부분을 책임지고 있다.
  • 이는 솔리드 원칙에 단일 책임 원칙을 위배 하는 코드이다.
  • 이렇게 될 경우 PlayerCtrl을 사용하게 되는 코드에 변경 또는 수정사항이 생겼을 때
    연쇄적으로 많은 부분들을 변경하고 또 수정해야하는 불가피함이 존재한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class PlayerMovement
{
public:
	void PlayerMove();
	void PlayerAxis();

private:
	Vector2 playerMoveDirection;
};

class PlayerRotation
{
public:
	void PlayerRotation();
	void PlayerQuaternion();

private:
	Vector2 playerRotateDirection;
}

// ...
  • 위와 같이 책임을 분리하여 모듈화 하면 어떤 수정사항이 생겼을 때
    플레이어 이동PlayerMove를 사용하는 코드에서 변경할 점이 없게 된다.
  • 이동 관련된 부분은 PlayerMovement 클래스만 수정하면 되기 때문이다.
  • 이 후 우리가 할일은 모듈화된 클래스들을 의존성이 적게 잘 연결만 해주면 된다.

  • 의존성 역전 원칙

  • 의존성 역전 원칙은 솔리드 원칙에 가장 마지막에 해당하는 부분인데 아직도 내용 이해가 힘들다.
  • 튜터님들에게도 계속해서 물어보고 했지만 역시 직접 사용을 해보지 않아서 그런 것 같다.
  • 그러던 도중 정호 튜터님에게 간단한 예제와 함께 내용 이해를 할 수 있었는데 다음과 같다.

  • 스위치는 문을 열기 위해 문을 직접 받아와 의존하고 있다.
  • 위 이미지대로 코드를 표현하면 다음과 같이 표현할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Switch
{
public:
	Door door;
	public bool isActivate;

public:
	void Toggle()
	{
		if(isActivate)
		{
			isActivate = false;
			door.Close();
		}
		else
		{
			isActivate = true;
			door.Open();
		}
	}
};

class Door
{
public:
	void Open()
	{
		cout << "문 열림" << endl;
	}

	void Close()
	{
		cout << "문 닫힘" << endl;
	}
}
  • 위 코드를 해석해보면 Switch클래스는 Toggle()에서 문이라는 객체에 대해 의존적이다.
  • 이 스위치는 오로지 문만을 위한 스위치이며 다른 스위치로 대체할 수 없다.
  • 스위치는 문이 열리든 불이 꺼지든 상관 없이 그저 토글(딸깍)만을 해야한다.
  • 그리고 내부적인 기능 동작은 문이나 전등에서 처리를 해야하고, 스위치는 누구인지 알 필요가 없어야한다.
  • 위 내용을 토대로 다음과 같이 코드를 만들 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class ISwitchable
{
public:
	virtual bool GetIsActive() const = 0;
	virtual void Activate() = 0;
	virtual void Deactivate() = 0;
};

class Door : public ISwitchable
{
public:
	bool GetIsActive() override { return _isActive; }
	void Activate() override
	{
		_isActive = true;
		cout << "문 열림" << endl;
	}
	void Deactivate() override
	{
		_isActive = false;
		cout << "문 닫힘" << endl;
	}

private:
	bool _isActive;
};

class Switch
{
public:
	ISwitchable switchClient;

public:
	void Toggle()
	{
		if(switchClient.GetIsActive())
			switchClient.Dectivate();
		else
			switchClient.Activate();
	}
}

  • Switch클래스는 이제 토글만 해주면 된다.
  • Switch Client로 누가 들어오든 (Door, Light 등) 상관 없이 토글만 실행시켜주면,
    내부적인 실제 로직은 내부에 들어가 있는 데이터가 직접 처리할 것이기 때문이다.

  • 이 내용을 이해하는데 너무 오랜시간이 걸렸고 실제로 사용하고 응용하기까진 시간이 더 걸릴 것 같다.
  • 객체-지향-설계에 대해서 좀 더 이해할 수 있었고 앞으로 코드를 짤 때 생각하면서 짜는 습관을 들일것이다.
This post is licensed under CC BY 4.0 by the author.