| ① 전 | ③ 후 |
|---|---|
| {code} private int _low, _high; boolean includes(int arg){ return arg >= _low && arg <= _high; } |
|
private int _low, _high;
boolean includes(int arg){
return arg >= getLow() && arg <= getHigh();
}
int getLow() {return _low;}
int getHigh() {return _high;}
|
h2. 2. Replace Data Value with Object
* 추가적인 데이터나 동작을 필요로 하는 데이터 아이템이 있을 때는 데이터 아이템을 객체로 바꾸어라.
■ 동기
* 예를 들면 Telephone 번호는 String으로 정의할 수 있지만 이와 관련된 동작(포맷팅, 지역코드 추출등)이
필요하다는 것을 알게된다. 이때 데이터 값을 객체로 바꾸어야한다.
!replacedata.gif!
|| ① 전 || ② 중 || ③ 후 ||
|
class Order...
private String _customer;
public Order (String customer){
_customer = customer;
}
public String getCustomer(){
return _customer;
}
public void setCustomer(String srg){
_customer = arg;
}
private static int numberOrderFor(Collection orders,
String customer){
int result = 0;
Iterator iter = orders.iterator();
while(iter.hasNext()){
Order each = (Order) iter.next();
if(each.getCustomer Name().equals(customer))
result++;
}
return result;
}
|
class Order..
private Customer _customer;
public Order (String customer){
_customer = new Customer(customer);
}
public String getCustomer(){
return _customer.getName();
}
public void setCustomer(String arg){
_customer = new Customer(arg);
}
class Customer{
private final String _name;
public Customer(String name){
_name = name;
}
public String getName(){
return _name;
}
}
|
class Order..
private Customer _customer;
public Order (String customerName){
_customer = new Customer(customerName);
}
public String getCustomer(){
return _customer.getName();
}
public void setCustomer(String customerName){
_customer = new Customer(customerName);
}
class Customer{
private final String _name;
public Customer(String name){
_name = name;
}
public String getName(){
return _name;
}
}
|
h2. 3. Change Value to Reference
* 동일한 인스턴스를 여러 개 가지고 있는 클래스가 있고 여러개의 동일한 인스턴스를 하나의 객체로 바꾸고 싶으면 그 객체를 참조객체로 바꾸어라.
* 같은 고객에 대한 여러가지 주문이 있으면 하나의 Customer 객체가 공유되도록 하고 싶다. (factory 메소드 생성)
!changevalue.gif!
|| ① 전 || ③ 후 ||
|
class Customer{
private final String _name;
public Customer(String name){
_name = name;
}
public String getName(){
return _name;
}
}
class Order..
private Customer _customer;
public Order (String customer){
_customer = new Customer(customer);
}
public String getCustomer(){
return _customer.getName();
}
public void setCustomer(String arg){
_customer = new Customer(arg);
}
private static int numberOrderFor(Collection orders,
String customer){
int result = 0;
Iterator iter = orders.iterator();
while(iter.hasNext()){
Order each = (Order) iter.next();
if(each.getCustomer Name().equals(customer)) result++;
}
return result;
}
|
// Order 가 Customer에 접근하기 위한 별도의 객체를 이용한다.
class Customer{
private static Dictionary _instances = new Hashtable();
public static Customer create (String name){
return new Customer(name);
}
//생성자 호출하는 부분을 팩토리 메소드로 호출하도록 바꿈
private Customer(String name){
_name = name;
}
}
// Customer 객체를 미리 만듬..(저자의 방법).
class Customer..
Static void loadCustomers(){
new Customer("Lemon Car Hire").store();
new Customer("Associated Coffe Machines").store();
new Customer("Bilston Gasworks").store();
}
private void store(){
_instances.put(this.getName(), this);
}
public static Customer create(String name){
return (Customer)_instances.get(name);
}
class Order{
_customer = Customer.create(cutomerName);
public Order(String customerName){
_customer = Customer.create(customerName);
}
}
|
h2. 4. Change Reference to Value
* 작고, 불변성(immutable)이고 , 관리하기가 어려운 참조객체가 있는 경우, 그것을 값 객체로 바꿔라.
* Reference 의 필요 없이 변하지 않는 데이터의 값을 가지는 object를 필요로 하는 경우 Reference의 Object를 Value object로 대체함.
■ 동기
* 참조 객체로 작업하는 것이 복잡해지면 참조에서 값으로 바꿀 이유가 될 수 있다.
!changereference.gif!
|| ① 전 || ② 후 ||
|
class Currency..
private String _code;
public String getCode(){
return _code;
}
private Currency(String code){
_code = code;
}
// Reference Object 이므로 instance를 얻기 위해
// 다음과 같은 형식으로 사용된다.
Currency usd = Currency.get("USD");
// 이 Currency 클래스는 instances의 리스트를 유지하고 있고,
// 생성자가 private으로 정의되어 있기 때문에 다른 클래스에서
// Currency object를 생성자로 정의할 수 없다.
new Currency("USD").equals(new Currency("USD")) //false 리턴
|
class Currency..
private String _code;
public String getCode(){
return _code;
}
//private으로 정의된 생성자를 public 생성자로 수정하여,
//Currency object를 정의하는 클래스에서 factory 메소드를 이용하지 않고,
//직접 Currency의 생성자를 이용한다.
public Currency(String code){
_code = code;
}
public boolean equals(Object arg){
if(!(arg instanceof Currency)) return false;
Currency other = (Currency)arg;
return (_code.equals(other._code);
}
// Reference가 아닌 Value 값을 가지고 equals와 hashCode 메소드를
//수행하도록 Object 클래스의 equals와 hashCode 메소드를 override한다.
public int hashCode(){
return _code.hashCode();
}
//equals와 hashCode 메소드는 반드시 함께 정의되어야 한다.
new Currency("USD").equals(new Currency("USD")) //true 리턴
|
h2. 5. Replace Array with Object
* 배열의 특정 요소가 다른 뜻을 가지고 있다면, 배열을 각각의 요소에 대한 필드를 가지는 객체로 바꿔라.
* 다른 정보를 담고 있는 array를 각각의 필드를 갖는 Object로 대체한다.
■ 동기
* Array는 유사한 데이터의 집합을 담고 있어야 한다.
* 데이터의 관리가 용이해야 한다.
|| ① 전 || ③ 후 ||
|
String[] row = new String[3];
row[0] = "Liverpool";
row[1] = "15";
|
Performance row = new Performance();
row.setName("Liverpool");
row.setWins("15");
|
* 변경 과정
|| ① || ② ||
|
String[] row = new String[3];
row[0] = "Liverpool";
String name = row[0];
|
//Array 제거
class Performance{
private String _name;
..................
}
public String getName(){
return _name;
}
public void setName(String arg){
_name = arg;
}
// Client Code...
row.setName("Liverpool");
String name = row.getName();
|
h2. 6. Duplicate observed Data
* GUI 컨트롤에서만 사용 가능한 도메인 데이터가 있고 도메인 메소드에서 접근이 필요한 경우, 그 데이터를 도메인 객체로 복사하고 Observer를 두어 두 데이터를 동기화하라.
■ 동기
* 사용자 인터페이스를 다루는 코드와 비즈니스 로직을 다루는 코드가 분리되어 있다.
* 시스템 개발 유리하고 유지보수가 쉽고 각각의 부분을 위한 개발자를 따로 가지고 있을 수 있다.
■ 도메인 클래스란?
* 객체의 정보를 포함
* 사용자가 입력한 정보를 모델에 전달하는 역할과 데이타베이스의 정보를 JSP에 전달하는 매개자 역할
!duplicate.gif!
|| ① 전 || ② 후 ||
|
public class IntervalWindow extends Frame{
java.awt.TextField _startField;
java.awt.TextField _endField;
java.awt.TextField _lengthField;
class SymFocus extends java.awt.event.FocusAdapter{
public void focusLost(java.awt.event.FocusEvent event){
Object object = event.getSource();
if( object == _startField)
StartField_FocusLost(event);
else if(object == _endField)
EndField_FocusLost(event);
else if(object == _lengthField)
LengthField_FocusLost(event);
}
void StartField_FocusLost(java.awt.event.FocusEvent event){
if(isNotInteger(_startField.getText())
_startField.setText("0");
calculateLength();
}
void EndField_FocusLost(java.awt.event.FocusEvent event){
if(isNotInteger(_endField.getText())
_endField.setText("0");
calculateLength();
}
void LengthField_FocusLost(java.awt.event.FocusEvent event){
if(isNotInteger(_lengthField.getText())
_lengthField.setText("0");
calculateEnd();
}
void calculateLength(){
try{
int start = Integer.parseInt(_startField.getText());
int end = Integer.parseInt(_lengthField.getText());
int length = end - start;
_LengthField.setText(String.valueOf(end));
}catch (NumberFormatException e){
}
}
void calculateEnd(){
try{
int start = Integer.parseInt(_startField.getText());
int length = Integer.parseInt(_lengthField.getText());
int end = start + length;
_endField.setText(String.valueOf(end));
}catch (NumberFormatException e){
}
}
}
|
//GUI에서 계산 로직을 분리하는 것이다.
//calculateLength와 calculateEnd를 별도의 도메인 클래스로 옮긴다는 것을 뜻한다.
class Interval extends Observable { //도메인 클래스
{
private String _end ;
private String _start ;
private String _length ;
......
public String getEnd() {
return _end ;
}
public setEnd(String arg) {
_end = arg ;
setChanged() ;
notifyObservers() ;
}
public void calculateEnd() {
try {
int start = Integer.parseInt(getStart()) ;
int end = Integer.parseInt(getEnd()) ;
int length = end . start ;
setLength(String.valueOf(length)) ;
} catch (...) { throw ... }
}
public void calculateLength() {
try {
int start = Integer.parseInt(getStart()) ;
int length = Integer.parseInt(getLength()) ;
int end = end + start ;
setEnd(String.valueOf(end)) ;
} catch (...) { throw ... }
}
public class IntervalWindow extends Frame implements Observer {
...............
private Interval _subject ; //도메인 클래스로의 링크
public IntervalWindow(...) {
_subject = new Interval() ;
_subject.addObserver(this) ;
update(_subject, null) ;
}
public void update(Observable observed, Object arg) {
_endField.setText(_subject.getEnd()) ;
}
//메소드를 호출하는 접근자 만듬
private String getEnd() {
return _subject.getEnd() ;
}
private setEnd(String arg) {
_subject.setEnd(arg) ;
}
...........................
private void calculateLength() {
_subject.calculateLength() ;
}
private void calculateEnd() {
_subject.calculateEnd() ;
}
...........................
}
|
h2. 7. Change Unidirectional Association to Bidirectional
* 각각 서로의 기능을 필요로 하는 클래스가 있는데 링크가 한쪽 방향으로만 되어 있는 경우, 반대 방향으로 포인터 추가하고, 수정자가 양쪽 세트를 모두 업데이트 하게 변경하라.
■ 동기
* p 232 참조
!changeunidirectional.gif!
|| ① 전 || ② 후 ||
|
class Order ...
Customer getCustomer() {
return _customer ;
}
void setCustomer(Customer arg) {
_customer = arg ;
}
Customer _customer ;
......
}
class Customer...
// Order object로의 reference 정보를 가지고 있지 않다.
...
}
|
// 방법1. Customer에 Order 컬렉션에 직접 접근
class Customer {
private Set _orders = new HashSet() ; // 한 Cutsomer가 여러 개의 Order 가질 수 있으므로
// 추가하는 필드는 컬렉션이어야 한다.
Set friendOrders() { // Customer에 Order 컬렉션에 직접 접근을 허용하는 helper 메소드
return orders ;
}
......
}
// 방법2. Customer를 통해서 링크를 수정하고 싶으면 제어 메소드를 호출
class Customer..
void addOrder(Order arg){
arg.setCustomer(this);
}
class Order...
private Set _customers = new HashSet() ;
void setCustomer(Customer arg) {
if ( _customer != null ) _customer.friendOrders().remove(this) ;
_customer = arg ;
if ( _customer != null ) _customer.friendOrders().add(this) ;
}
void addCustomer(Customer arg) {
arg.friendOrders().add(this) ;
_customers.add(arg) ;
}
void removeCustomer(Customer arg) {
arg.friendOrders().add(this) ;
_customers.remove(arg) ;
}
|
h2. 8. Change Bidirectional Association to Unidirectional
* 서로 링크를 가지는 두 개의 클래스에서 한 쪽이 다른 한쪽을 더 이상 필요로 하지 않을 때는 불필요한 링크를 제거하라.
!changebidirectional.gif!
|| ① 전 || ② 후 ||
|
class Order ...
Customer getCustomer() {
return _customer ;
}
void setCustomer(Customer arg) {
if ( _customer != null ) _customer.friendOrders().remove(this) ;
_customer = arg ;
if ( _customer != null ) _customer.friendOrders().add(this) ;
}
private Customer _customer ;
............
}
class Customer ...
void setOrder(Order arg) {
arg.setCustomer(this) ;
}
private Set _orders = new HashSet() ;
Set friendOrders() {
return _orders ;
}
|
//1) 코드의 customer object를 제거하기 위해 customer object를 parameter로
//넘기는 방법을 사용할 수 있다.
class Order ...
double getDiscountedPrice() {
return getGrossPrice() * (1-_customer.getDiscount()) ;
}
//수정
class Order ...
double getDiscountedPrice(Customer customer) {
return getGrossPrice() * (1-customer.getDiscount()) ;
}
//이 메소드는 Customer 클래스 안에서 호출되기 하는 것이 더욱 좋다.
//Customer 클래스 안에서는 자기 자신을 parameter로 넘겨주는 것이 쉽기 때문
class Customer ...
double getPriceFor(Order order) {
Assert.isTrue(_orders.contains(order)) ;
return order.getDiscountedProce() ;
}
//수정
class Customer ...
double getPriceFor(Order order) {
Assert.isTrue(_orders.contains(order)) ;
return order.getDiscountedPrice(this) ;
}
//2) parameter로 object를 넘겨 받지 않고, getting 메소드를 이용한다.
class Order ...
Customer getCustomer() {
Iterator iter = Customer.getInstance().iterator() ;
while ( iter.hasNext() ) {
Customer each = (Customer)iter.next() ;
if ( each.containsOrder(this) ) return each ; }
return null ;
}
|
h2. 9. Replace Magic Number with Symbolic Constant
* 특별한 의미를 가지는 숫자 리터럴이 있으면, 상수를 만들고, 의미를 잘 나타내도록 이름을 지은 다음, 숫자를 상수로 바꿔라.
|| ① 전 || ② 후 ||
|
double potentialEnergy(double mass, double height){
return mass * 9.81 * height;
}
|
double potentialEnergy(double mass, double height){
return mass * GRAVITATIONAL_CONSTANT * height;
}
static final double GRAVITATIONAL_CONSTANT = 9.81;
|
h2. 10. Encapsulate Field
* public 필드가 있는 경우, 그 필드를 private으로 만들고, 접근자를 제공하라.
* 객체 지향에서는 데이터를 절대로 public으로 하지 말라고 함.
다른 객체가 데이터 값에 접근하고 변경 할 수 있게 되어서..
|| ① 전 || ② 후 ||
|
public String _name
|
public String _name
public String getName() { return _name;}
public void setName(String arg) { _name = arg;}
|
h2. 11. Encapsulate Collection
* 컬렉션(Collection)을 리턴하는 메소드가 있으면, 그 메소드가 읽기전용 뷰(read-only view)를 리턴하도록 만들고, add/remove 메소드를 제공하라.
!encapsulate.gif!
|| ① 전 || ② 후 ||
|
class Person ...
public Set getCources() {
return _courses ;
}
public void setCourses(Set arg) {
_courses = arg ;
}
private Set _courses ;
...............
//클라이언트 코드에서는 Person 클래스를 다음과 같이 사용하게 된다.
Person kent = new Person() ;
Set s = new HashSet() ;
s.add(new Course, "Smalltalk Programming", false)) ;
s.add(new Course, "Appreciating Single Malt", true)) ;
kent.setCourses(s) ;
Course refact = new Course("Refactoring", true) ;
kent.getCourses().add(refact) ;
kent.getCourses().remove(refact) ;
|
class Person {
...............
private Set _courses ;
public void addCourse(Course arg) {
_courses.add(arg) ;
}
public void removeCourse(Course arg) {
_courses.remove(arg) ;
}
public void initializeCourses(Set arg) {
Assert.isTrue(_courses, isEmpty()) ;
_courses.addAll(arg) ;
}
public void setCourses(Set arg) {
Assert.isTrue(_courses.isEmpty()) ;
Iterator iter = arg.iterator() ;
while ( iter.hasNext() ) {
addCourse((Course)iter.next()) ;
}
public Set getCourses() {
return Collection.unmodifiableSet(_courses) ;
}
//클라이언트 코드에서는 Person 클래스를 다음과 같이 사용하게 된다.
Person kent = new Person() ;
kent.addCourse(new Course("Smalltalking Programming", false)) ;
kent.addCourse(new Course("Appreciating Single Malts", true)) ;
//기존의 kent.getCourse().add(new Course("Brutal Sarcasm", false)) ;
//클라이언트 코드를 kent.addCourse(new Course("Brutal Sarcasm", false)) ;
//으로 대체한다.
//또한 person.getCourses()를 호출하는 기존 클라이언트 메소드를
int numberOfAdvancedCourses(Person person) {
Iterator iter = person.getCourses().iterator() ;
int count = 0 ;
while ( iter.hasNext() ) {
Course each = (Course)iter.next() ;
if ( each.isAdvanced() ) count++ ;
}
return count ;
}
//Person 클래스의 메소드로 Move Method 한다.
public int numberOfAdvancedCourses() {
Iterator iter = getCourses().iterator() ;
int count = 0 ;
while ( iter.hasNext() ) {
Course each = (Course)iter.next() ;
if ( each.isAdvanced() ) count++ ;
}
return count ;
}
|
h2. 12. Replace Record with Data Class
* 전통적인 프로그래밍 환경에서의 레코드 구조에 대한 인터페이스가 필요한 경우, 그 레코드를 위한 데이터 객체를 만들어라.
h2. 13. Replace Type Code with Class
* 클래스의 동작에 영향을 미치지 않는 숫자로 된 타입 코드(numberic type code)가 있으면, 숫자를 클래스로 바꿔라.
■ 동기
* 숫자를 파라미터로 받는 임의의 메소드가 있다고 가정하면 이는 코드의 가독성을 떨어뜨리고
버그의 위험성도 지니고 있다.
만약 숫자를 클래스로 대체한다면 컴파일러는 클래스에 대해서 타입을 검사하게 된다.
이 클래스에 factory 메소드를 제공함으로써 유효한 instance가 생성되는지 객체가 전달되었는지를
정적으로 확인할 수 있다.
!replacetype.gif!
|| ① 전 || ② 후 ||
|
class Person{
public static final int O = 0;
public static final int A = 1;
public static final int B = 2;
public static final int AB = 3;
private int _bloodGroup;
public Person (int bloodGroup){
_bloodGroup = bloodGroup;
}
public void setBloodGroup(int arg){
_bloodGroup = arg;
}
public int getBloodGroup(){
return _bloodGroup;
}
}
clas BloodGroup{
public static final BloodGroup O = new BloodGroup(0);
public static final BloodGroup A = new BloodGroup(1);
public static final BloodGroup B = new BloodGroup(2);
public static final BloodGroup AB = new BloodGroup(3);
private static final BloodGroup[] _values = {O, A, B, AB};
priavet final int _code;
private BloodGroup(int code){
_code = code;
}
public int getCode(){
return _code;
}
public static BloodGroup code(int arg){
return _values[arg];
}
}
//Person을 사용하는 클라이언트 코드에 대한 작업
Person thePser = new Person(Person.A);
thePerson.getBloodGroupCode();
thePerson.setBloodGroup(Person AB);
| {code} class Person{ public static final int O = BloodGroup.O.getCode(); public static final int A = BloodGroup.A.getCode(); public static final int B = BloodGroup.B.getCode(); public static final int AB = BloodGroup.AB.getCode(); |
private BloodGroup _bloodGroup;
public Person (int bloodGroup){
_bloodGroup = BloodGroup.code(bloodGroup);
}
public void setBloodGroup(int arg){
_bloodGroup = BloodGroup.code.(arg);
}
public int getBloodGroup(){
return _bloodGroup.getCode();
}
}
//수정 코드
Person thePser = new Person(BloodGroup.A);
thePerson.getBloodGroupCode().getCode();
thePerson.setBloodGroup(BloodGroup AB);
|
h2. 14. Replace Type Code with Subclasses
* 클래스의 동작에 영향을 미치는 변경 불가능한(immutable) 타입 코드가 있다면, 타입 코드를 서브클래스로 바꾸어라.
■ 동기
* 클래스의 동작에 영향을 미치지 않는 타입 코드가 있다면,
Replace Type Code with Class를 사용할 수 있다.
* 그러나 타입 코드가 동작에 영향을 미치는 경우에는 다형성을 사용하여 여러가지
동작을 처리하도록 하는 것이 좋다.
!subclasses.gif!
|| ① 전 || ② 후 ||
|
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;
}
|
class Employee ..
private int _type;
static final int ENGINEER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
//생성자를 팩토리 메소드로 바꾼다.
Employee create(int type){
switch(type){
case ENGINEER:
return new Engineer();
case SALESMAN:
return new Salesman();
case MANAGER:
return new Manage();
default:
throw new IOllegalArgumentException("Incorrect");
}
}
private Employee(int type){
_type = type;
}
abstract int getType(); //추상 메소드를 만든다.
class Engineer extends Employee{
int getType(){
return Employee.ENGINEER;
}
}
|
h2. 15. Replace Type Code with State/Strategy
* 클래스의 동작에 영향을 미치는 타입 코드가 있지만 서브클래싱을 할 수 없을 때는 타입 코드를 스테이트(State) 객체로 바꿔라.
!state.gif!
|| ① 전 || ② 후 ||
|
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;
}
//Client Code
int payAmount(){
switch(_type){
case ENGINEER:
return _monthlySalary;
case SALESMAN:
return _monthlySalary + _commission;
case MANAGER:
return _monthlySalary + _bonus;
default:
throw new RuntimeException("Incorrect Employee");
}
}
Employee(int type){
setType(type);
}
int getType(){
return _type;
}
void setType(itn arg){
_type = arg;
}
|
int payAmount(){
switch(getType()){
case ENGINEER:
return _monthlySalary;
case SALESMAN:
return _monthlySalary + _commission;
case MANAGER:
return _monthlySalary + _bonus;
default:
throw new RuntimeException("Incorrect Employee");
}
}
//스테이트 클래스를 선언
abstract class EmployeeType{
abstract int getTypeCode();
}
//그리고 나서 서브클래스를 만든다.
class Engineer extends EmployeeType{
int getTypeCode(){
return Employee.ENGINEER;
}
}
class Salesman extends EmployeeType{
int getTypeCode(){
return Employee.SALESMAN;
}
}
class Manager extends EmployeeType{
int getTypeCode(){
return Employee.MANAGER;
}
}
// 서브 클래스들을 Employee에 연결한다.
class Employee..
private EmployeeTyep _type;
int getType(){
return _type.getTypeCode();
}
void setType(int arg){
switch(arg){
case ENGINEER:
_type = new Engineer();
break;
case SALESMAN:
_type = new Salesman();
break;
case MANAGER:
_type = new Manager();
break;
default:
throw new IllegalArgumentException("Incorrect Employee Code");
}
}
// 팩토리 메소드를 만들고 set 메소드를 적절히 변경한다.
class Employee ..
void setType(int arg){
_type = EmployeeType.newType(arg);
}
class Employee ...
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");
}
}
static final int ENGINNER = 0;
static final int SALESMAN = 1;
static final int MANAGER = 2;
|
h2. 16. Replace Subclass with Fields
* 상수 데이터를 리턴하는 메소드만 다룬 서브클래스가 있으면, 그 메소드를 수퍼클래스가 있으면, 그 메소드를 수퍼클래스의 필드로 바꾸고 서브클래스를 제거하라.
■동기
* 기능을 추가하거나, 동작의 변화를 허용하기 위해서 서브클래스를 만든다.
동작 변화의 한 형태는 상수메소드(하드 코딩된 값을 리턴하는 메소드)이다.
상수메소드는 유용하기는 하지만, 상수 메소드를 포함하고 있는 서브클래스는 존재할 가치가 있을 만큼 충분한 일을 하는 것이 아니다.
서브클래스를 사용하면서 생기는 여러 복잡성을 제거할 수 있다.
!fields.gif!
|| ① 전 || ② 중 || ③ 후 ||
|
abstract class Person{
abstract boolean isMale();
abstract char getCode();
...
class Male extends Person{
boolean isMale(){ //상수 메소드
return true;
}
char getCode(){ //상수 메소드
return 'M';
}
}
class Female extneds Person{
boolean isMale(){
return false;
}
char getCode(){
return 'F';
}
Person kent = new Male();
|
class Person{
//각각 상수 메소드에 대한 필드를 수퍼클래스에 선언
private final boolean _isMale;
private final char _code;
//각각의 서브클래스에 대한 팩토리 메소드만듬
static Person createMale(){
return new Male();
}
static Person createFemale(){
return new Female();
}
//Protected 생성자 추가
protected Person(boolean isMale, char code){
_isMale = isMale;
_code = code;
}
}
class Male extneds Person{
Male(){
super(true, 'M');
}
boolean isMale(){
return true;
}
}
class Female extneds Person{
Female(){
super(false, 'F');
}
boolean isMale(){
return false;
}
}
Person kent = Person.createMale();
|
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');
}
//Protected 생성자 추가
protected Person(boolean isMale, char code){
_isMale = isMale;
_code = code;
}
}
Person kent = Person.createMale();
|
h2. 문서에 대하여
* 이 문서의 내용은 [Refactoring\- 나쁜 디자인의 코드를 좋은 디자인으로 바꾸는 방법|http://refactoring.com] 교재를 스터디 하면서 정리한 내용 입니다.
* 최초작성자 : [임영미]
* 최초작성일 : 2007년 12월 04일
* 이 문서는 [오라클클럽|http://www.gurubee.net] [자바 웹개발자 스터디|2007년 하반기 - 제2차 자바 웹개발 스터디] 모임에서 작성하였습니다.
* 이 문서를 다른 블로그나 홈페이지에 퍼가실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.