고양국제고

GTT 6 개발 스토리 #5: 이게 진짜 프로그래밍이지

카루-R 2022. 8. 4. 20:42
반응형

환영합니다, Rolling Ress의 카루입니다.

농담으로만 꺼냈던 기능을 구현해보았습니다. 내신 점수 산출기인데요, 사실 이미 개인의 학기별 내신 점수는 알고 있을 거예요. 그런데 대학에 따라서 반영하는 방법 등이 다르죠. 과목에 제한을 건다든지, 진로선택과목은 다르게 반영을 한다든지, 혹은 등급이 아니라 원점수에 관한 변환점수를 가지고 성적을 산출한다든지. 그걸 모두 GTT 안에서 해결할 수 있도록 변환내신점수 산출기를 개발하고자 합니다.

근데 문제는 똑같은 인터페이스를 중복으로 사용하니까 유지보수에 골치가... 오른쪽 클래스뷰에 보시면 ConetButton, MyGodButton, TaskButton이 보이죠. 근데 사실 저 셋이 거의 같습니다. 공유하고 있는 틀이 있는데, 기능이 다르다보니 중복된 코드가 쓸데없이 많이 들어갔다고 봐야 할까요. 프로그래밍에서 중복코드는 죄악입니다. 그래서 이걸 해결하기 위해 가장 좋은 방법은? 인터페이스와 상속입니다. 인터페이스가 필요한진 모르겠는데 일단 일반클래스/추상클래스 + 인터페이스 조합으로 설계하려고 해요.

머리보다 손이 먼저 움직이면 골치아파집니다. 나중에 수정하기 힘들거든요.

세 클래스에서 동시에 작업을 해야 하기 때문에 5분할로 나누었습니다. 양쪽은 제가 그냥 확인하기 위한 패널들이고, 가운데 두 개 뭔가 쫙 써있는 게 현재 버튼 코드들, 그리고 맨 왼쪽은 이제부터 작성할 "공통 버튼"입니다. 실제로 개체화할 순 없지만 이 둘, 그리고 추후에 생길 새 버튼의 특징까지 묶을 수 있도록.

자, 이렇게 하면 Button -> { TaskButton, ConetButton } 이었던 관계가 Button -> GttButton -> { TaskButton, ... }으로 바뀝니다. 이제 공통된 것들을 추려서 옮겨봅시다.

우선 private으로 선언된 공통된 상수들을 protected로 바꾸어 옮겨줍시다.

근데 이거 애매하네요. 버튼이 담고 있는 기능이 있는데, 그게 각각 달라요. ConetHelp, TodoTask.. 이럴 때 쓰는 방법이 있죠. 이걸 제네릭으로 바꾸어봅시다.

아무거나 들어오면 안 되니까 우선 기반 클래스에 IButtonData를 달아주고요.

이렇게 제네릭 매개변수에 제약을 걸어줍시다. 그럼 T 자리에 엉뚱한 타입이 오는 걸 막을 수 있어요.

프로그램이 망가질 수도 있어서 Git commit 했습니다. 그럼 이제 본격적으로 초기화 구문을 GttButton<T>으로 옮겨봅시다. 생성자 위임을 통해 코드를 간결하게 만들 거예요.

추상 클래스의 생성자로 모든 코드를 옮긴 뒤, 구현되지 않은 메서드는 자식 클래스에서 알아서 정의할 수 있도록 추상 메서드를 남겨두었습니다. 이렇게 하면 추상 클래스에서는 메서드의 본체가 없어도 컴파일이 되죠.

파생 클래스의 생성자는 깔끔해진 모습을 볼 수 있습니다. 특히 왼쪽 같은 경우엔 아무 일도 하지 않는 대신 부모 클래스에게 모든 초기화를 맡기는 모습을 볼 수 있습니다.

새롭게 구성한 GttButton<T>의 생성자입니다. 어차피 abstract 클래스라 인스턴스 생성이 불가하니, 명시적으로 protected 레벨로 선언했습니다. 우선 오늘은 여기까지 하도록 하죠. 피곤하네요.

반응형