2024. 9. 25. 21:02ㆍ내일배움캠프
이번주 '스탠다드반' 의 세션(특강)은 '객체지향' 에 관한 내용이었다. 정확히 말하면 객체지향을 사용했을 때 얻는 이점을 튜터님의 라이브코딩과 설명으로 알아보는 것 이었는데, 감사하게도 이해를 돕기위해 '게임(스타크래프트)' 을 예시로 하여 세션을 진행해 주셨다. 세션간 튜터님이 작성한 코드와 내가 작성한 코드는 해당 링크를 통해 확인할 수 있다.
무튼 해당 게시글을 작성한 이유는 세션 마지막에 튜터님께서 '간단한 과제' 를 내어주셨기 때문이다. 솔직히 과제의 느낌보다는 이번 세션 라이브코딩을 수강생들이 한 명이라도 더 작성해보고 느끼라고 내주신 느낌이 강하다. 튜터님이 과제를 주시며 하신 말씀은 아래와 같다.
- "여러분이 찾아보길 바라는 마음으로 '캡슐화' 를 완전히 만족하지 않도록 코딩을 했으니 찾아보고 수정해 주세요."
- "각 유닛(마린, 질럿, 저글링)에 해당하는 클래스를 생성하고 본인이 원하는 필드 및 메서드를 구현해 주세요."
그래서 한 번 위의 내용을 해결해 본 과정을 작성해 보겠다.
※ 참고 : 내용의 혼동이 있을 수 있어 말하자면 튜터님이 작성하신 코드와 내가 작성한 코드의 클래스 및 필드명은 아래와 같이 다르나 주어진 역할은 같다고 보면 된다.
- Marin.java = Unit.java
- int hp = int healthPoint
0. 튜터님의 코드
먼저 튜터님의 코드중 '마린' 유닛을 다루는 'Marin' 클래스를 살펴보자.
나의 경우 '빨강색 박스' 안의 내용이 'Marin' 클래스를 '제대로 캡슐화하지 못한 부분' 이라 생각했다. 개인적으로 캡슐화는 객체가 가진 자원을 '보호' 한다는 생각을 가지고 있는데, 여기서 말하는 '보호' 는 "클래스 외부로부터의 (무분별한) 접근" 에 대한 보호를 말한다. 그러니 '캡슐화' 했다는 것은 충분히 외부로부터의 접근을 차단할 수 있다는 것이라 생각한다.
하지만 '빨강색 박스' 를 보면 'hp, dmg' 변수의 '접근 제어가' 가 설정되지 않았다. 자바에서는 '접근 제어자' 를 설정하지 않으면 'default(같은 패키지 내에서만 접근 허용)' 로 설정되는데 그렇게 되면 아래와 같은 문제가 발생한다.
이렇게 외부(클래스)에서 생성한 객체의 필드에 직접 접근해 해당 필드를 수정(접근)해 버릴 수 있다. 물론 현재 Main 클래스와 Unit 클래스는 같은 패키지에 위치한 상태이다.
"그럼 Unit 클래스를 다른 패키지로 옮기면 되는거 아니야?" 라는 생각을 할 수도 있지만 솔직히 도긴개긴이다. 다른 패키지로 옮긴다 하여도 옮긴 패키지에 다른 클래스를 추가하면 해당 클래스에서도 접근을 허용하게 되고, Unit 클래스만을 갖는 패키지를 만드는 것은 오히려 여러 제약사항을 만들어 버리게 된다. 그리고 패키지는 관련 클래스 등을 묶어 관리하기 위해 존재하는 것이지 데이터의 접근을 막기위해 사용하는 것이 아니므로 위의 방법은 적절하지 않다 생각한다.
그래서 아래와 같이 적절한 '접근 제어자' 를 설정해야 한다. 말은 적절하게 설정해야 한다지만 나의 경우 '캡슐화, 객체화' 를 할 때는 대부분 'private' 를 사용하는 듯 하다.
결과적으로 튜터님이 말씀하신 '일부러 만든 빈틈(캡슐화에 대한)' 은 위의 내용을 말씀하신게 아닌가 생각된다. 다음은 아래의 '파란색 박스' 를 살펴보자.
특정 유닛에 대한 공격을 받았을 때 수행되는 메서드들이 박스안에 존재하는데 공격 받는 유닛에 따라 메서드를 생성하다 보니 메서드 수행부가 동일한 메서드들이 생겼다. 이는 '중복 코드' 로 볼 수 있으며 큰 수정을 안하고 중복을 제거한다면 전달 받는 파라미터(유닛 객체)에 따라 데미지를 계산해 유닛의 'hp' 를 차감하면 될 것이다.
하지만 그래도 '중복 코드' 에 대한 문제가 또 있는데, 튜터님께서는 각 유닛별로 클래스를 생성했고, 각 클래스에 동일한 필드와 동일한 기능의 메서드를 구현해 두셨다. 비슷한 유형의 '행위' 때문에 각 클래스 별로 해당 '행위' 들을 작성해 '중복 코드' 가 생긴 것이다.
위에 이야기한 두 가지 내용은 '추상화' 를 통해 어느 정도 해결할 수 있을 듯 하다. 그래서 아래의 '1. 추상클래스 상속, 2. 인터페이스 상속' 에서 마저 이야기해보자.
1. 추상 클래스 상속
처음에는 바로 인터페이스를 통한 추상화를 통해 문제를 해결하고 끝내려 했으나 유닛 클래스별로 동일하게 갖는 필드와 같은 행위를 하는 메서드들이 다수 있었기에 '추상 클래스' 활용을 먼저 진행해 보았다.
각 유닛 클래스별로 동일하게 갖는 필드 및 메서드는 구현하고, 유닛 클래스별로 다르게 구현되어야 할 메서드는 '추상 메서드' 로 작성한 '추상 클래스' 이다.
이보다 더 확장하면 또 달라지긴 하겠지만, 현재 상황에서 각 유닛 클래스는 '체력(healthPoint), 공격력(attackPoint), 등급(unitGrade)' 속성을 가지며 '체력, 공격력' 을 파라미터로 전달 받아 생성할 수 있다. 또한 현재 유닛의 상태(status)를 출력할 수 있고 다른 유닛에게 공격 받을 수 있다. 그래서 해당 내용들을 'Unit' 클래스의 빨강색 박스 안에 구현했다.
그리고 개인적으로 추가한 부분이지만 각 유닛별로 '업그레이드(upgrade)' 시 '체력, 공격력' 이 다르게 증가하고, 유닛이름이 다르기에 해당 내용들은 파란색 박스 안에 '추상 메서드' 로 작성했다.
위의 추상 클래스를 상속 받은 클래스의 경우에는 아래와 같이 구현했다.
추상 클래스인 'Unit' 을 상속 받았기에 추상 클래스의 '추상 메서드' 인 'upgrade(), getUnitName()' 을 구현하였다. 또한 'upgrade()' 메서드의 경우 상속 클래스의 필드에 접근할 필요가 있어, 'Unit' 클래스의 필드의 접근 제어자를 'protected' 로 설정해둔 것이다. 'protected' 를 사용했기에 추상 클래스와 상속 클래스 외에는 접근을 막을 수 있다.
또한 각 유닛별로 동일하게 갖는 속성 및 행위는 'Marin' 클래스에 존재하지 않는 것을 확인할 수 있다. '중복 코드' 문제를 해결한 것이다. 그러면서도 각 유닛별로 다르게 수행되어야 할 기능은 별도로 관리할 수 있다.
2. 인터페이스 상속
Unit 인터페이스를 상속할 각 유닛 클래스들이 반드시 구현해야 할 추상 메서드들을 선언해 주었다. 그리고 해당 인터페이스를 상속한 클래스는 아래와 같이 작성하였다.
유닛이 갖는 속성들을 필드에 작성해주고 상속받은 인터페이스가 갖는 '추상 메서드' 를 모두 구현해 주었다. 여기서 확인할 부분은 인터페이스 상속 클래스의 필드의 접근 제어자를 모두 'private' 로 설정에 외부의 접근을 막아 해당 클래스의 메서드를 통하지 않으면 외부에서 필드에 접근할 수 없다.
'중복 코드' 의 경우 확인해 보면, 인터페이스 상속만 추가됬을 뿐 튜터님이 작성한 코드처럼 동일한 목적의 변수와 동일한 기능을 수행하는 메서드가 각 클래스별로 여전히 구현되었기에 "중복 코드를 제거한 것이 맞나?" 라 하면 "아니다" 라고 말할 것이다.
하지만 그렇다고 해서 인터페이스를 활용하면 중복 코드를 제거할 수 없는 것이 아니다. 사실 주어진 과제가 "각 유닛 클래스에 내가 넣고 싶은 상태와 행위를 넣어서 나만의 유닛 만들어 오기" 이므로 위와 같이 인터페이스를 활용하는 단계까지 진행했을 뿐, 이후에 중복 작성되는 메서드를 클래스에서 분리해 별도의 클래스를 생성하거나 유닛 생성에 역할을 가진 클래스는 하나만 두고 유닛 생성에 대한 정보를 enum 클래스의 상수 필드에 저장해두고 이를 가져와 사용하는 방식을 사용한다면 충분히 중복 코드를 제거할 수 있을 것으로 보인다.
3. 마무리
이렇게 기존의 튜터님이 작성한 코드에 '추상 클래스' 와 '인터페이스' 를 적용해 보며 '캡슐화' 와 '중복 코드' 에 대한 문제를 해결/파악해 보았는데 '추상 클래스' 의 경우 상속 받는 클래스가 많아질 수록 오히려 '제한 사항' 이 많아질 것 같다는 생각을 했으며, '인터페이스' 의 경우 상속 받는 클래스가 많아져도 '제한 사항' 이 '추상 클래스' 에 비해 비교적 많아지지 않을 것 같다는 생각을 해볼 수 있었다.
평소 인터페이스를 더 자주 사용한 것도 영향이 아예 없진 않겠지만, 당장의 상황에서 구현을 하는 것은 확실히 '추상 클래스' 가 편했다. 하지만 이후의 요구사항으로 인한 확장을 염두하고, 유지보수 측면에서 바라봤을 때, '인터페이스' 가 좀 더 편리할 것으로 보인다.
'내일배움캠프' 카테고리의 다른 글
[내일배움캠프] 일정 관리 앱 - DB 세팅 (0) | 2024.09.27 |
---|---|
[내일배움캠프] TIL - 24.09.26(목) (0) | 2024.09.26 |
[내일배움캠프] TIL - 24.09.24(화) (0) | 2024.09.24 |
[내일배움캠프] 숫자 야구 게임 구현 - 리팩토링(3) (0) | 2024.09.23 |
[내일배움캠프] 숫자 야구 게임 구현 - Level.04 요구사항 반영 (0) | 2024.09.22 |