코드를 변경할 때 주석도 함께 변경하면 좋지만, 바쁠 때 충분히 주의하지 않으면 주석까지 유지 보수하기는 힘듦
주석이 구현 시점과 멀어질수록, 주석을 거짓말을 할 가능성이 높음
주석이 낡아버리기 전에 구현 변경시 주석도 함께 변경하는 것이 좋음
최대한 의도가 제대로 전달될 수 있게 클래스와 메서드의 이름을 짓고, 주석을 달아야함
코드의 동작을 설명하는 주석은 코드를 변경할 때마다 주석도 변경해야할 것
그러므로 주석은 말 전하기 게임과 같음 과장 되거나 누락되어 실제 내용과 바뀔 수 있음
로직을 그대로 설명하는 주석은 코드를 이해하는데 별다른 도움이 되지 않고 오히려 어려움
메서드의 이름만으로 의도를 전달하기 힘들 때에는 의미를 다시 설명하는 주석을 달기 쉬운데
이렇게 되면 앞서 말한 것처럼 낡은 주석이 되기 쉬움
이럴 땐 그냥 메서드의 이름 자체를 수정하는 것이 좋음
코드가 읽힐 때는 주로, 기본적으로 유지보수 할 때와 사양을 변경할 때 읽힘
이럴 때 중요사항은 '어떤 의도를 갖고 동작하는가?'
또한, '안전하게 변경하려면 무엇을 주의해야하는가?'
/**
* @params 매개변수 설명
* @throws throw되는 예외 설명
* @returns 리턴 값 설명
*/
메서드는 반드시 현재 클래스의 인스턴스 변수를 사용하도록 설계해야함
완전 생성자 패턴을 사용하여 생성자에 가드를 만들어 두면, 인스턴스 변수를 안전하게 사용하기 위한 첫걸음을 뗀 것
다른 클래스의 인스턴스 변수를 변경하는 메서드를 작성하고 싶다면, 변경된 내용을 다루는 새로운 인스턴스를 생성하고 이를 리턴하는 형태로 구현하는 것이 좋음
public class Person {
private String name;
private int age;
// 생성자
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 나이 변경 메서드
public Person changeAge(int newAge) {
// 변경된 내용을 다루는 새로운 인스턴스를 생성하여 반환
return new Person(this.name, newAge);
}
public static void main(String[] args) {
// 초기 Person 인스턴스 생성
Person person1 = new Person("Alice", 30);
System.out.println("Before change: " + person1.name + ", " + person1.age);
// 나이를 변경하는 메서드 호출
Person person2 = person1.changeAge(35);
// 변경된 인스턴스 출력
System.out.println("After change: " + person2.name + ", " + person2.age);
// ★ 기존 인스턴스는 변경되지 않음 ★
System.out.println("Original instance: " + person1.name + ", " + person1.age);
}
}
가변 인스턴스 변수 등을 변경하는 메서드는 의도하지 않게 다른 부분에 영향을 줄 수 있기 때문에
불변을 활용하여 예상치 못한 동작 자체를 막을 수 있어야 함
getter/setter는 '다른 클래스를 확인하고 조작하는 메서드 구조'가 되기 쉬움
그러므로 메서드를 호출하는 쪽에서는 복잡한 처리를 하지 않는 것이 좋음
차라리 호출되는 메서드에서 복잡한 처리를 하는 형태로 만들어야 함
여러 개발도구에서 getter/setter를 자동으로 만들어주는 기능에 '캡슐화'라는 이름을 붙이고 있지만, 이것은 캡슐화가 아님
캡슐화는 데이터와 메서드를 하나의 단위로 묶어서 데이터를 보호하는 것뿐만 아니라,
적절한 접근 제어자를 사용하여 데이터의 무결성을 보장하고, 객체의 상태를 일관되게 유지하는 것을 포함
따라서 Getter와 Setter를 자동으로 생성하는 기능은 캡슐화의 일부를 자동화하는 데 도움이 될 수 있지만,
캡슐화를 완전히 이해하고 적절히 활용하기 위해서는 개념적인 이해와 함께
접근 제어자(private, protected 등)의 사용과 데이터의 무결성을 고려하는 것이 중요함
// 상태 변경과 추출을 동시에 하는 메서드는 여러 문제의 원인이자 사용자도 쓰기 힘든 메서드가 되어 버림
// 예를 들어, 변경만 하고 싶거나, 추출만을 하고 싶을 때 사용할 수 없음
int gainAndGetPoint() {
point += 10;
return point;
}
-> 그러므로 좋을 것이 하나없는 형태
커맨드 쿼리 분리 (CQS, Command-Query Seperation)
메서드는 커맨드 또는 쿼리 중에 하나만 하도록 설계해야 한다는 패턴
고로 분리를 하자
// 커맨드
gainPoint(): void {
this.point += 10;
}
// 리턴
getPoint(): number {
return point;
}
매개변수를 불변으로 만들어야 함
변경하고 싶으면 불변 지역 변수를 만들고 여기에 변경 값을 할당하는 형태로 구현
의미를 알 수 없으니 전략 패턴을 사용하는듯, 다른 구조로 설계
매개변수로 null을 전달하지 않도록 설계 null에 의미를 부여해선 안 됨
차라리 ~.EMPTY라고 표현하는 것처럼 구현
매개변수는 입력값으로 사용되는 것이 기본이고 출력값으로 사용하면 가독성에 혼란을 줄 수 있음
매개변수가 많다는 것은 여러 가지 기능을 처리한다는 의미, 매개변수가 많아진다면 차라리 별도의 클래스로 만드는 방법을 검토
독자적인 자료형을 사용하여 의도를 명확하게 나타내야함
Price price = productPrice.add(otherPrice);
DiscountedPrice discountedPrice = new DiscountedPrice(price);
매개변수처럼 null도 리턴하지 않는 게 좋음
어떤 값으로 여러 의미를 나타내는 것은 중의적이라고 함
중의적은 사람에게 혼란을 줄 수 있고 이를 위해 조건분기를 써도 조건 분기가 여럿 늘어남
잘못된 상태에서는 어떤 관용도 베풀어서는 안 됨, 리턴 값으로 오류 값을 리턴하지 말고 곧바로 예외를 발생 시켜야 함
메서드 이름 설계
동사 + 목적어 형태의 메서드 이름은 책무와 상관없는 로직을 구현하도록 유도할 수 있는 이름
최대한 이름이 동사 하나가 되도록 메서드와 클래스를 설계하는 것이 좋음