멧돼지 피드백 관련 학습
1.MVC 패턴
기존에도 패턴에대해서 공부하고 MVVM에 대해서 주로 공부했지만 그냥 관습적으로 사용하던 안드로이드에서만 사용 했을뿐
이렇게 모든 곳에서 사용할 수 있을것이라고 생각조차 하지 못하였다.(이런 코틀린 프로그램에서도 MVP나 MVVM도 적용할수 있는거자나?......)
당장 짠 프로그램도 MVC와 유사하게 짜여져 있지만 일부 도메인 로직이 controller에 들어가있는 상황이었다.
또한 페어프로그래밍을 통해서 Car객체를 만들어서 사용했지만(고마워요 하티)
혼자 코딩했다면 이런 data들을 controller가 들고있는 실수를 범했을것이다.
이러한 실수를 하지않고 MVC패턴을 제대로 사용해 보기위해 각 요소들이 갖는 역할들과 내가 하지못했던 문제점을 찾아보려한다.
MVC 패턴은 GUI 데스크톱 어플리케이션이 등장하면서 생긴 패턴 이라고한다. -> 당연히 맞는말이네 신기하다.
MVC 패턴은 역사적으로 변화가 있었고 과거의 MVC와 현대의 MVC는 많은 차이가 있다고 한다.
이 그림이 현대적인 MVC 패턴에대한 그림이라고 하는데 사실상 글자를 빼고 본다면 MVVM이랑 거의 비슷한 형태이다(하지만 내부적으로는 추구하는 방향과 느낌도 다르고 MVVM 의 핵심은 결국 데이터 바인딩과 뷰모델의 재활용이라고 생각한다. -> controller은 재활용이고 뭐고 그냥 비대하다.)
이게 Model,View,Controller 이세가지 요소들이 각각 어떤 역할을 하고 어떠한 것들을 지켜나가야하는지 살펴보자
모델은 즉 모든 데이터를 들고있으며 그 데이터들을 처리할수 있는 함수를 들고있는 객체들이다.
도메인 로직을 다루는 객체들을 뜻한다.
-> 즉 데이터를 들고있고 그 데이터를 처리하는 부분을 모두 뜯어내서 각각 문맥별로 분리해서 객체를 만들어주면 Model 뚝딱 완성이다.
(데이터를 들고있는 변수들과 그 변수들을 조작하는 함수들로 꽉차 있을 것이다.)
view 나 controller 에 대해서 알고있으면 안된다.(의존하면 안된다)
즉 진짜 히키코모리처럼 혼자 데이터들고 그것만 변경하는 변태처럼 만들어줘야한다.(주변객체들과 관계가 단절되어야한다 유일하게 말걸어주는건 controller)
이친구는 화면에 나오는 입력 출력과 관련된 로직들(뷰로직) 만 들고있어야한다. 본인의 할일 이외의 행위(괜히 데이터 들고 있기) 이런것들이 있어서는 안된다.
이 친구 역시 데이터와는 진짜 상관없이 print,readline 이런식으로 진짜 순수하게 화면에서 데이터 받아서 넘기기 ,데이터 받아서 출력하기 이런 행위를 하는 함수들만 있어야한다.
이친구도 화면에 관련된일만하고 아무와도 소통하지않으며 오로지 controller랑만 소통하는 바보로 만들어야한다.(다른 객체들과 의존성이 없어야한다.)
이 구역의 인싸 controller이다.
다른 데이터를 다루는 객체들과, 화면을 다루는 객체들이 여기서 서로 각각 필요에의해 호출되며 소통하게 된다 즉 컨트롤러가 모두 인스턴스를 만들어놓고 데이터를 모델에서 받아서 화면에 뿌리는등 적절한 함수들을 모두 호출하고 전체적인 컨트롤을 하는 역할을 한다.
이렇듯 모든 코드들의 정류소 역할을 하기 때문에 아무래도 컨트롤러는 비대해질수 밖에 없다.
이런식으로 각각의 역할을 정확히 구분하고 model과 view 를 정말 고립시켜나간다면 좀더 좋은 코드를 만들어 나갈수 있을것이다.
자료출처:https://velog.io/@seongwon97/MVC-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80
2.전략패턴(개방폐쇄원칙과 세트)
-> 즉 인터페이스를 통해서 각각의 동작을 미리미리 정의해놓고 (추상화) 사용처에서는 인터페이스에 의존하도록 코드를 작성한다.
각각 인터페이스를 상속받은 클래스들을 여러개를 만들어서 각각을 다른 알고리즘을 사용한다던지 하여서 다른방식으로 수행할수 있도록(ex: test 할때 random 같은 것들 내가 의도한대로 stubclass를 통해서 얻어내기) 하여
쉽게쉽게 원하는 상황을 수행할 수 있는 코드들을 갈아끼우는 방법이다.
이러한것들이 추상화를 시키지 않고 그냥 의존성이 꼬여있다면 나중에 코드를 갈아 끼울때 정말 슬픈 상황이 발생하게 될것이다.
그림을 보면 3가지 알고리즘을 각각 구현하지만 메서드의 사용처인 context 에서는 그냥 strategy의 함수만 호출해주면 되는것이다.
이방법을 사용하려면 당연히 의존성은 주입되여야 할것이다.
전략패턴의 장점을 정리해보면
- OCP를 준수하기 위한 사용하는 방식
- 전략을 쉽게 바꿀 수 있도록 해주는 디자인 패턴
- 행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴
- 새로운 기능의 추가가 쉬워진다(기존 코드 보존가능)
이렇게 엄청 킹짱 패턴이라 디자인 패턴의 꽃이라고 불린다고 한다.
그리고 컴포지션(has-a)을 이용한 방법이 전략패턴
상속(is-a)을 이용한 방법을 템플릿 메서드 패턴이라고 한다.
컴포지션과 상속이 헷갈린다면 다음글을 참고하자
https://devjino.tistory.com/19
이러한 패턴을 사용하게되면 OCP(개방 폐쇄 원칙) 에 위배되지 않으면서 시스템이 거대해졌을때 메서드의 중복을 해결할 수 있다고 한다.
->쉽게 말하자면 기능이 추가되어도 기존코드는 변경되지 않고 확장하기 쉽게 만들수 있다는 것이다.
근데 또 OCP는 무엇인가?
객체지향하면 맨날 나오는 solid 원칙의 O를 담당하고 있는 담당일진 (Open Closed Principle) 개방 폐쇄 원칙이다.
수정에는 닫혀 있어야 한다는것은 기존 코드의 변경으로인해 이코드에 의존하고있는 다른코드들이 줄줄이 바뀌면 안된다는 뜻이다.
라이브러리의 코드들이 수정된다고 라이브러리 사용처의 코드가 바뀌는일은 없듯이 개방폐쇄 원칙을 통해 기존 코드는 변경하지않으면서(기존 기능을 날려버리지 않으면서) 기능을 추가 할수 있도록 설계되어야 한다는 뜻이다.)
-> 전략패턴을 보면서 어떻게 기존기능은 냅두면서 기능을 추가할 수 있는지 감이 올것이다.
예시가 궁금하다면 이글을 참고하자
https://blog.itcode.dev/posts/2021/08/14/open-closed-principle
-> 결론적으로 상속을 이용하든 구현을 이용하든 추상화와 패턴들을 잘이용하여 이러한 원칙을 지켜나가는것이 객체지향적으로 올바른 코드라고 할수있다.
출처:https://brownbears.tistory.com/574
3.의존성 주입의 장점
의존성 주입은 위에서와 같이 항상 추상화와 세트로 움직일때 의미가 비로소 살아나는것 같다.
의존성 주입의 장점은 피부로 느껴지지만 어버버 하며 답변을 못하는것을 대비하여 자료화 된것을 킹갓 테코블에서 따왔다
DI의 장점
1. 의존성이 줄어든다.
앞서 설명했듯이, 의존한다는 것은 그 의존대상의 변화에 취약하다는 것이다.(대상이 변화하였을 때, 이에 맞게 수정해야함) DI로 구현하게 되었을 때, 주입받는 대상이 변하더라도 그 구현 자체를 수정할 일이 없거나 줄어들게됨.
2. 재사용성이 높은 코드가 된다.
기존에 BurgerChef 내부에서만 사용되었던 BurgerRecipe을 별도로 구분하여 구현하면, 다른 클래스에서 재사용할 수가 있다.
3. 테스트하기 좋은 코드가 된다.
BurgerRecipe의 테스트를 BurgerChef 테스트와 분리하여 진행할 수 있다.
4. 가독성이 높아진다.
BurgerRecipe의 기능들을 별도로 분리하게 되어 자연스레 가동성이 높아진다.
출처:https://tecoble.techcourse.co.kr/post/2021-04-27-dependency-injection/
4.객체에서 getter setter을 어떻게 관리해야하고 피할수있는 방법은 무엇이 있을까?
기존에 getter와 setter를 기준없이 사용하는것은 좋지않다(객체지향적으로) 그래서 기준없이 그냥 막연히 안쓰려고 노력하고 뭔가 없애려는 방법만 생각했었다.
하지만 여러가지 자료들을 읽어보고 고민해본결과 순수하게 출력이나 객체의 값을 참고해야할 때는 getter는 쓸수밖에없다.물론 setter는 자제하는게 맞는거 같다.(객체 내부에서 상태값을 알아서 바꾸는 형식으로 처리하자)
이에 대한 글이다.
https://tecoble.techcourse.co.kr/post/2020-04-28-ask-instead-of-getter/
자 이제 막연한 두려움은 없애고 필요할때 적절히 getter와 setter를 사용하는 방법을 살펴보자
자바는 매번 필드와 getter,setter를 직접 생성해줘야했지만
코틀린의경우 프로퍼티 기반 언어이기 때문에 사실상 변수를 선언할 때 getter setter가 자동으로 생성된다.
그러므로 이것을 잘 이용해서 불필요하게 getter와 setter를 다시 만드는 일을 없게 해보자
1. val로 선언한 프로퍼티에 getter만 사용하고싶은 경우
어짜피 val 로 선언한 변수는 외부든 내부든 값을 변경할수 있는 setter가 생성되지 않기에 두려움없이 외부접근을 위해 private를 없애고 접근을 열어주면 getter를 만들어주지 않아도된다.
2. var로 선언한때 getter만 사용하고 싶은경우
- setter를 private로 놔주면 객체의 내부에서는 set을 자유롭게 해줄수 있지만 외부에서는 setter가 막히므로 편리하게 이용할수 있다.
이렇게 외부에 setter을 노출하지 않도록 오버라이딩 할수 있는데
밑에나오는 코드리뷰처럼 원시값에 더 적절한것같다(리스트는 backing properties로!!!!)
외부에서 접근할수 있는것은 리스트로 놓고 getter를 오버라이딩 하여 내부에서 사용할수 있는 MutableList를 사용한다면 이또한 setter를 외부에 노출시키지 않을수 있다. -> 하지만 원시값에는 굳이 복잡한 이방법보다 private set이 적절하다고 생각한다.
우리 팀원 코건에게 달린 코드리뷰인데 이렇게 해야겠다고 싶어서 가져왔다 ㅎㅎㅎ
참고로 이렇게 getter setter를 오버라이딩 하여 로직을 추가 할수도 있으니 참고하자
5.방어적 복사
이펙티브 코틀린에서 언듯 봤었던것으로 기억하는 방어적 복사를 조언해 주셔서 이번기회에 좀 짚어보려고한다
방어적 복사란?
생성자의 인자로 받은 객체의 복사본을 만들어 내부 필드를 초기화하거나,
getter메서드에서 내부의 객체를 반환할 때, 객체의 복사본을 만들어 반환하는 것이다.
방어적 복사를 사용할 경우, 외부에서 객체를 변경해도 내부의 객체는 변경되지 않는다.
기본적인것은 이런 의미인데 불변객체라는것이 굉장히 강력하다는것은 로또 피드백 글에서 300번 이야기 할거다.
-> 로또 피드백 글도 참고하자
아 중요한것으로 인자값으로 들어오는 값들은 얕은복사로 들어오는것이다 -> 그래서 이런일들이 벌어진다.
+ 또한 각 리스트 등등의 요소들에서 깊은복사 얕은복사 문제가 있으니 이글을 참고하자!!
https://tecoble.techcourse.co.kr/post/2021-04-26-defensive-copy-vs-unmodifiable/
step2
1.펙토리 메서드 패턴
캡슐화와 팩터리 메서드에 대한 피드백을 받았다.
적용하려는데 팩토리 메서드 관련 패턴이 너무 많아서 원하는 정보를 구분하는데 애를 먹었다.
키워드로 추상 팩토리 메서드 패턴,팩토리 메서드 패턴, 정적 팩토리 메서드 패턴 등등 정말 많았다.
결론적으로 내가 원했던것은 자바라면 정적 팩토리 메서드 패턴
코틀린이라면 정적이라는 개념이 없으므로 companion object와 함께 다니는 팩토리 메서드 였다.
네이밍을 잘지켜서 사용하고 다음 로또 피드백에서 좀더 효율적으로 팩토리 메서드 패턴을 사용하는 방법을 정리했으니 참고하자
당장 사용법은 이 블로그를 참고하면된다.
https://kyucumber.github.io/posts/book/effective-kotlin/effective-kotlin-item-33
2.인라인화
시간이없어서 생략했는데 추후 공부하고 추가해볼 예정이다.
3.오류처리
내가 좋아하는 오류처리 부분도 조언해주셨다 이 부분은 조만간 테코톡 자료에서 박살나게 만들어볼것같다.
추천해주신 글
https://elizarov.medium.com/kotlin-and-exceptions-8062f589d07
하티 피드백 관련 학습
1.god-object(단일 책임 원칙)
solid 가 계속 등장하는데 그중 S 에 해당하는 단일 책임 원칙을 위반하고 여러개의 책임을 다 가져가게되는 클래스를 god-object 라고한다.
Single Responsibility Principle (단일 책임 원칙):
이 원칙의 정의는 모든 클래스는 단 한 가지의 책임만 갖고, 클래스 안에 정의되어 있는 모든 기능은 이 하나의 책임을 수행하는 데 집중되어 있어야 한다는 것이다.
-> 쉽게 말하면 그냥 기능을 많이 쪼개서 클래스가 한가지 일만 하라는것이다.
이부분을 들어가면 같이 수정해야하는건 묶고,수정할때 갈라지는건 분리해줘야한다 블라블라 예시 이런거 나와야하는데 이부분은 책을 보면서 마저 공부해보도록 하겠다.
근데 이말이 참 맞는거같다 이름이 명확하다면 여러개의 기능이 비집고 들어갈수가 없다고 생각한다. 이름이 길어서 불편하더라고 좀 자세히 써보도록 해야겠다.
2.상수 관리
상수 관리하는 방법은 워낙 관점도 여러가지고 이렇게 저렇게 생각이 달라서 뚜렷한 주관이 있어야겠지만
개인적으로 전체적으로 사용되는 게임의 룰 같은 상수들은 종류별로 파일을 여러개로 분리해서 관리하는것이 제일 관리하기 편한것같다.
그리고 만약 각 클래스의 내부에서만 사용된다면 동반객체로 관리하는 방법을 사용하는것이 관리하는 최고의 방법이라고 생각한다.
나의 킹갓 제너럴 리뷰어님의 견해는 이렇다.
3.maxOf{}
참 좋은 api 하나 발견!!!
4.람다에서 변수명도 신경쓰자
함상 it을 그냥 썻었는데 이것도 변수명을 확실히 사용해주면 더 좋은 코딩이 될거같아서 이런게 눈에 보이는 리뷰어분이 대단해 보였다.
코건 피드백 관련 학습
1. 원시값 포장
블로그에 글이 너무 잘 정리되어있어 이글을 참고하면 될것같다!!!
링링 피드백 관련 학습
이런식으로 생각할수도 있구나 라고 생각했다 진짜 좋은 말인거같다 상태를 가지고 있지 않다면(인스턴스화 할 필요없는 클래스라면) object로 생성해도 되겠구나
'우테코 > level1(코틀린)' 카테고리의 다른 글
[우테코] 템플릿 메서드 패턴 간단요약 (0) | 2023.03.16 |
---|---|
[우테코]02/17 level1 네번째 수업(로또 피드백) (5) | 2023.02.23 |
[우테코]로또 미션 코드리뷰 관련 학습내용 (0) | 2023.02.22 |
[우테코]02/14 level1 세번째 수업 (두번째 미션 자동차경주) TDD,OOP (0) | 2023.02.20 |
[우테코]02/10 level1 두번쨰 수업 자동차 경주 피드백 (0) | 2023.02.20 |