코딩의 기술
1. 읽기 좋은 코드를 작성하는 기술
- 결정문의 사용해서 If 문을 제거 / 예상 값을 배열에 9까지 경우의 수로 저장해서 해결 . 재미 있네.
enum Hand {
Rock = 0,
Scissors = 1,
Paper = 2
};
enum Result {
Win,
Lose,
Draw
};
Result judgement(Hand my, Hand target){
if (my ==target) return Draw:
if (my == Rock && target == Scissors) return Win ;
if (my == Scissors && target == Paper) return Win ;
if (my == Paper && target == Rock) return Win ;
return Lose;
}
Result judgment(Hand my, Hand target){
static const Result result[3][3] = {
//바위 , 가위 보 (상대방)
{Draw, Win, Lose }. // 바위(자신)
{Lose, Draw, Win }, // 가위
{Win, Lose, Draw } // 보
};
return result[my][target];
}
2. 간단한 설계를 위한 원칙과 패턴
- 직접하지 말고 명령하라. /위임하라.
- 상속보다는 이양을 사용 / Template pattern => Strategy pattern
// 상속으로 설계
Class Robot {
public update(){}
public move(){}
}
Class CleanRobot : public Robot{
virtual void move() override {}
}
Class CombatRobot : public Robot{
virtual void move() override {}
}
// 위양으로 설계
Class Robot {
RoboBehavior* behavior_;
// Creator
Robot(RobotBehavior* behavior) :
behavior_ (behavior} {}
void update()
{
behavior_->move();
}
}
// Robot Behavior interface , Strategy pattern
Class RobotBehavior {
virtual void move()
};
class CleanerBehavior : public RobotBehavior {
virtual void move() override{
//clean
}
}
class CombatBehavior : public RobotBehavior {
virtual void move() override{
//battle
}
}
// Create Instance
Robot cleanerRobot(new CleanerBehavior());
Robot combatRobot(new CombatBehaviro());
- 상속 관계는 '컴파일할 때 결함' 합니다. 실행 중에는 구현 객체를 변경하지 못한다.
- 이양을 사용하면 실행 중에도 구현 객체를 변경할 수 있다.
- 다른 유형의 로봇클래스를 만들고 싶을 때로 행동과 관련한 플래스를 재사용할 수 있다.
- 상속은 부모 클래스로부터 '기능상혹'을 의미하지만 , 추상 인터페이스는 '역할 구현'을 의미한다.
3. 객체 지향 설계의 원칙
- 단일 책임의 원칙(SRT, Single Responsibility Principle) - 클래스를 변경해야 되는 이유는 한가지
- 개방. 폐쇠의 원칙(OCP, Open-closed principle) - 확장에 관해서는 열려있고, 변경에 대해서 닫여있다.
- 리스코프 치환 원칙 (LSB, Liskov Substitution Principle) - 부모를 클래스로 치환한 상태에서도 정상 작동해야 한다.
- 인터페이스 분리 원칙(ISP , Interface Segregation Principle) - 클래스의 사용자에게 불필요한 인터페이스는 공개하지 말라
- 의존 관계 역전 원칙(DIP, Dependency Inversion Principle) - 상위 모듈은 하위 모듈에 의존하지 않는다.
- 데메티르 법직 - 직접적인 친구(자기자신, 자신이 가지는 클래스 , 매개변 수로 전달한 클래스)와만 관련한다.
- Singleton pattern을 사용하면 이 법칙을 위반한다. => Logger 같은 클래스는 이법칙의 예외일 것 같은데. 이 법칙은 참고로만.
- 디자인 패턴
- State pattern과 Strategy pattern은 구현방법이 거의 비슷하지만, 사용하는 상황이 다르므로 구분해서 사용한다. Gog의 디자인 패턴은 구현 방법으로 패펀을 구분해놓은 것이 아니다.
- 생성자를 완전한 상태 만들기
- 호출된 시점에 완전한 상태 , 별도의 초기화 함수를 호출하거나 Setter를 사용해서 설정해야 작동하는 클래스는 주의
- 어떤 순서로 맴버 함수를 호출해도 문제없이 작동하게 한다. 호출 순서에 제한이 있는 클래슨 사용자에게 부담, 그리고 버그의 원인
- 멤버 함수 호출 수서와 관련한 대처 방법
- 상속을 사용한 예 (Template Method pattern)
- 이양을 사용한 예(Strategy pattern)
- 결합을 생각
- 결합의 좋고 나쁨은 해당 클래스의 단위 테스트가 얼마나 간단할지 생각해보면 쉽게 판다.
- 테스트가 어려운 클래스는 독립성이 낮고, 부적절한 결합 상태일 가능성이 크다.
- 부적절한 결합이 발견되었다면 , 추상 인터페이스를 추가함으로써 직접적인 결합 관계를 끊어버린다.
- 코드의 수명
- 재사용할 가치가 있는 부분은 시간을 들여 설계, 코드를 재사용할 수 있게 설계하는 것은 상당한 시간과 노력이 필요.
- 항상 비용대비 효율성을 고민
- 작업의 편의성
- 코드의 단순함 보다는 작업의 편의성/ 쉽게 공동 작업을 할 수 있는 설계를 하고, 변경 또는 수정할 때 실수가 발생하지 않도록 설계
- 하나의 방법에 대한 집착
- 객체 지향을 사용하는 것이 항상 좋은 선택은 아님, 특징에 맞는 프로그래밍 언어와 개발환경을 선택 => 함수형 프로그래밍 등등
- 여러 언어를 조합하는 경우도 많음 , 목적을 달성하기 위해 가장 효율적인 방버이 무엇일까?를 생각
- 최소한의 코드만 작성
- 어떻게 코드를 작성할지가 아니라 => 어떻게 하면 코드를 작성하지 않을까가 중요 / 부수코드를 만들지 말자..
- 원칙과 패턴에 너무 얽매이자 보면 이상한 집착이 생겨서 단순한 코드도 복잡하게 만드는 경향이 있다.
- 원칙을 사용한 경유와 사용하지 않을 경우, 클래스를 분할하는 경우와 분할하지 않은 경우 중에 단순한 쪽을 선택
- 고수일수록 , 좋은 의미의 편법을 잘 활용한다. 쉽게 복잡해지는 부분, 미래에 변경될 수 있는 부분은 시간을 두고 설계하지만 , 변경 가능성이 작은 부분 등은 시간을 들이지 않고 간단하게 끝내버린다.