2. 마법 숫자를 기호 상수로 전환
(Replace Magic Number with Symbolic Constant)
• 마법 숫자는 특수한 값을 갖는 숫자를 일컫는 용어로 , 여러 곳에서 논리적으로 같은
숫자를 참조해야 할 경우가 많은데 , 수정해야 할 상황이 오면 끔찍한 일이 발생한다 .
• 즉 , 상수를 하드코딩하지 말자 .
class TestA{
public boolean isOverFlow(int curCount){
if(curCount > 10){
return true;
}
return false;
}
}
class TestB{
public int getMaxCount(){
return 10;
}
}
class TestA{
public boolean isOverFlow(int curCount){
if(curCount >
CommonConst.MAX_COUNT){
return true;
}
return false;
}
}
class TestB{
public int getMaxCount(){
return CommonConst.MAX_COUNT;
}
}
interface CommonConst{
public int MAX_COUNT = 10;
}
3. 필드 캡슐화
(Encapsulate Field)
• 데이터는 절대로 public 으로 선언하면 안된다 . 반드시 private 형태에서 setter,
getter 를만들어 사용해야 한다 .
• 얼핏 생각하기엔 , publi 으로 하든 , setter, getter 를 만들든 외부에서 값을 변경할 수
있는 것은 매한가지 아닌가 라고 생각할 수 있지만 , 실제 코딩을 해보면 그렇지 않다
는 것을 느낄 수 있다 . 예를 들어 , 아래와 같은 사소한 실수를 미연에 방지할 수도 있
고 , 무엇보다 변수를 호출하거나 세팅할 때 일괄 처리해야 할 코드가 추가될 경우 해
당 변수의 setter, getter 를 수정하는 것만으로 대응할 수 있다 .
class MyObject{
public int mId = -1;
}
MyObject myObj = new MyObject();
myObj.mId = 2; // 빌드에러 없이 컴파일 된
후 엄청난 side effect 를 마주하게 됨 .
class MyObject{
private int mId = -1;
public int getId(){
return mId;
}
public void setId(int id){
mId = id;
}
}
MyObject myObj = new MyObject();
myObj.mId = 2; // 빌드 에러 발생
4. 컬렉션 캡슐화
(Encapsulate Collection)
• 컬렉션 ( 배열 , 리스트 , 세트 , 벡터 ) 의 getter 는 객체 자체를 반환해서는 안된다 .
• Java 는 기본적으로 pass by value 지만 , object 를 pass 할 때는 object 의 reference 가
value 로써 passing 된 다는 것을 기억하자 .
• Thread safe 한 컬렉션 객체를 만들기에도 더 용이하다 .
public void foo(Dog d) {
d.getName().equals("Max"); // true
d = new Dog("Fifi");
d.getName().equals("Fifi"); // true
}
Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Max"); // true
public void foo(Dog d) {
d.getName().equals("Max"); // true
d.setName("Fifi");
}
Dog aDog = new Dog("Max");
foo(aDog);
aDog.getName().equals("Fifi"); // true
5. 레코드를 데이터 클래스로 전환
(Replace Record with Data Class)
• 프로그래밍 환경에서 흔히 사용되는 레코드 구조를 덤 데이터 개체로
전환한다 .
• 덤 데이터란 , 데이터가 거의 들어 있지 않은 객체로 , 기능 추가 없이
데이터에 public 속성이나 읽기 / 쓰기 메서드로 접근할 수 있다 . 프로
그래머 입장에선 객체지향 프로그래밍 개념의 정통적 개념에 위배된
다 . 패턴 / 안티패턴의 사용은 캡슐화에 완전히 위배된다 .
6. 분류 부호를 클래스로 전환
(ReplaceType Code with Class)
• 숫자형 분류번호를 별도의 클래스로 전환하면 코드가 상당히 이해하기
쉬워진다 .
• 책이 집필된 시점의 java version 엔 enum type 이 없어서인지 enum
type 같은 class 를 별도로 만들어서 설명하고 있다 . 참고만하고 그냥
enum type 을 쓰면 될듯 ?
public enum Blood {
O(1), A(2), B(3), AB(4);
private int value = 0;
Blood(int i) { value = i; }
public int getNumber() { return value; }
public static Blood get(int value) {
if (value == O.getNumber())
return O;
else if (value == A.getNumber())
return A;
else if (value == B.getNumber())
return B;
else if (value == AB.getNumber())
return AB;
return AB;
}
}
7. 분류 부호를 하위클래스로 전환
(ReplaceType Code with SubClasses)
• 분류 부호가 클래스 기능에 영향을 줘서 클래스로 전환 기법을 사용할
수 없을 경우 재정의를 통해 조금씩 다른 기능을 처리하게 한다 .
• 이와 같이 이 기법은 다형성을 가능케하는
사전작업으로 시행할 때가 많다 .
public class Employee {
private int _type;
static final int ENGINEER = 0;
static final int SALESMAN =
1;
static final int MANAGER = 2;
Employee (int type) {
_type = type;
}
}
public class Engineer extends Employee {
protected Engineer(int type) {
super(type);
}
@Override
public int get_type() {
return Engineer.ENGINEER;
}
}
public abstract class Employee {
private int _type;
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
protected Employee(int type) {
_type = type;
}
public static Employee createEmployee(int type) {
// if(type == ENGINEER) return new Engineer(type);
// else return new Employee(type);
switch (type) {
case ENGINEER:
return new Engineer(type);
case SALESMAN:
return new Manager(type);
case MANAGER:
return new Manager(type);
default:
throw new IllegalArgumentException(“Incorrect Type Code”);
}
}
abstract int get_type();
}
8. 분류 부호를 상태 / 전략 패턴으로 전환
(ReplaceType Code with State/Strategy)
• 이 기법은 “분류 부호를 하위클래스로 전환"과 비슷하지만 , 분류 부호
가 객체 수명주기 동안 변할 때나 다른 이유로 하위클래스로 만들 수 없
을 때 사용한다 .
class Employee {
private int _type;
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
Employee (int type) {
_type = type;
}
int payAmount() {
switch (_type) {
case ENGINEER:
return _monthlySalary;
case SALESMAN:
return _monthlySalary + _commission;
case MANAGER:
return _monthlySalary + _bonus;
default:
throw new RuntimeException("Incorrect Employee");
}
}
9. 분류 부호를 상태 / 전략 패턴으로 전환
(ReplaceType Code with State/Strategy)
class Engineer extends EmployeeType {
int getTypeCode () {
return Employee.ENGINEER;
}
}
class Manager extends EmployeeType {
int getTypeCode () {
return Employee.MANAGER;
}
}
class Salesman extends EmployeeType {
int getTypeCode () {
return Employee.SALESMAN;
}
}
class EmployeeType...
static EmployeeType newType(int code) {
switch (code) {
case ENGINEER:
return new Engineer();
case SALESMAN:
return new Salesman();
case MANAGER:
return new Manager();
default:
throw ..
}
}
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
}
class Employee...
int getType() {
return _type.getTypeCode();
}
void setType(int arg) {
_type = EmployeeType.newType(arg);
}
int payAmount() {
switch (getType()) {
case EmployeeType.ENGINEER:
return _monthlySalary;
case EmployeeType.SALESMAN:
return _monthlySalary + _commission;
case EmployeeType.MANAGER:
return _monthlySalary + _bonus;
default:
throw new RuntimeException("Incorrect Employee");
}
}
10. 하위클래스를 필드로 전환
(Replace Subclass with Fields)
• 단순히 상수 메서드만 존재하는 하위클래스는 상위클래스의 필드로 전
환하고 , 하위클래스는 완전히 삭제하면 된다 .
abstract class Person {
abstract boolean isMale();
abstract char getCode();
...
}
class Male extends Person {
boolean isMale() {
return true;
}
char getCode() {
return 'M';
}
}
class Female extends Person {
boolean isMale() {
return false;
}
char getCode() {
return 'F';
}
}
abstract class Person {
private final boolean _isMale;
private final char _code;
static Person createMale(){
return new Person(true, 'M');
}
static Person createFemale(){
return new Person(false, ‘F');
}
...
}