posted by 셀로브 2013. 10. 22. 01:39


ricksGuitars-choices.zip



이제 원하는 기능을 수행하고 있으니, "2) 객체지향의 기본원리를 사용해서 소프트웨어를 유연하게 하라." 하도록 해봅시다.


책을 보면 3가지를 말하고 있습니다.

 객체는 자신의 이름에 해당되는 일만 해야합니다. 자동차 객체는 전진 후진 기능은 있지만, 차를 사고 파는 기능은 자동차 객체에게는 필요없는 기능입니다.


이전 소스코드를 떠올려보면, Search함수의 매개변수로 Guitar객체를 사용하고 있습니다. 실제 비교에는 serialNumber와 price는 사용하지 않는데 말이죠.


즉 Search함수에서는 Guitar의 특정 속성만 필요하지만, Guitar객체 전체를 매개변수로 보내고 있기때문에 사용되지 않는 속성이 빈번히 일어나게 됩니다.


이렇게 사용되지 않는 속성이 많다면, 설계의 문제가 있음을 의심해 보아야 합니다.


Guitar객체에서 기타의 속성정보들을 분리하여 캡슐화하도록 하겠습니다.


다음 클래스다이어그램을 한번 보세요.




이전의 기타클래스에 필수적인 serialNumber와 pirce를 제외한 특성들에 해당되는 멤버변수를, GuitarSpec이라는 클래스로 묶어주었습니다.

멤버변수가 이동되었으니, Guitar객체의 해당 멤버변수 접근함수역시 옮겨줍니다.

그리고 Guitar객체 안에 GuitarSpec을 멤버객체로 가지게 하는것입니다.


다음은 변경된 소스코드 입니다.



Guitar.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Guitar {
 
  private String serialNumber;
  private double price;
  GuitarSpec spec;
 
  public Guitar(String serialNumber, double price, 
                Builder builder, String model, Type type,
                Wood backWood, Wood topWood) {
    this.serialNumber = serialNumber;
    this.price = price;
    this.spec = new GuitarSpec(builder, model, type, backWood, topWood);
  }
 
  public String getSerialNumber() {
    return serialNumber;
  }
 
  public double getPrice() {
    return price;
  }
 
  public void setPrice(float newPrice) {
    this.price = newPrice;
  }
 
  public GuitarSpec getSpec() {
    return spec;
  }
}


GuitarSpec.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class GuitarSpec {
 
  private Builder builder; 
  private String model;
  private Type type;
  private Wood backWood;
  private Wood topWood;
 
  public GuitarSpec(Builder builder, String model, Type type,
                    Wood backWood, Wood topWood) {
    this.builder = builder;
    this.model = model;
    this.type = type;
    this.backWood = backWood;
    this.topWood = topWood;
  }
 
  public Builder getBuilder() {
    return builder;
  }
 
  public String getModel() {
    return model;
  }
 
  public Type getType() {
    return type;
  }
 
  public Wood getBackWood() {
    return backWood;
  }
 
  public Wood getTopWood() {
    return topWood;
  }
}
 

Inventory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Inventory {
  private List guitars;
 
  public Inventory() {
    guitars = new LinkedList();
  }
 
  public void addGuitar(String serialNumber, double price,
                        Builder builder, String model,
                        Type type, Wood backWood, Wood topWood) {
    Guitar guitar = new Guitar(serialNumber, price, builder, 
                               model, type, backWood, topWood);
    guitars.add(guitar);
  }
 
  public Guitar getGuitar(String serialNumber) {
    for (Iterator i = guitars.iterator(); i.hasNext(); ) {
      Guitar guitar = (Guitar)i.next();
      if (guitar.getSerialNumber().equals(serialNumber)) {
        return guitar;
      }
    }
    return null;
  }
 
  public List search(GuitarSpec searchSpec) {
    List matchingGuitars = new LinkedList();
    for (Iterator i = guitars.iterator(); i.hasNext(); ) {
      Guitar guitar = (Guitar)i.next();
      GuitarSpec guitarSpec = guitar.getSpec();
      if (searchSpec.getBuilder() != guitarSpec.getBuilder())
        continue;
      String model = searchSpec.getModel().toLowerCase();
      if ((model != null) && (!model.equals("")) &&
          (!model.equals(guitarSpec.getModel().toLowerCase())))
        continue;
      if (searchSpec.getType() != guitarSpec.getType())
        continue;
      if (searchSpec.getBackWood() != guitarSpec.getBackWood())
        continue;
      if (searchSpec.getTopWood() != guitarSpec.getTopWood())
        continue;
      matchingGuitars.add(guitar);
    }
    return matchingGuitars;
  }
}



자 이렇게 캡슐화를 하고 난 후에는, search()함수에 GuitarSpec객체만 넘겨주어 원하는 기타들을 찾을 수 있게 됩니다.


어떤가요. 앞서말한 말("객체는 자신의 이름에 해당되는 일만 해야 합니다") 지켜진것 같나요?


이전과 같은 기능을 수행하지만, 프로그램은 더욱 유연해 졌습니다.




다음 내용 공부하기




posted by 셀로브 2013. 10. 21. 08:09

이전 소스코드상태에서 고객이 Electronic Type의 기타를 원한다고 가정해봅시다. 가게에 Electronic Type의 기타가 여러개 있더라도 사용자는 맨 처음 검색되는 기타만 볼 것입니다.

그 기타가 마음에 들지 않으면 구입하지 않겠네요. 다음 클래스 다이어그램처럼 소스코드를 변경해 봅시다.




search함수가 List형태로 반환하도록 해봅시다.


즉, 필요한 것은 고객의 요구에 맞는 기타를 모조리 반환하도록 변경하는 것입니다.


아래와같이 수정해줍니다.


Inventory.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Inventory {
    private List guitars;
    
    public Inventory(){
        guitars = new LinkedList();
    }
 
    // 새로운 기타를 생성하고 목록에 등록 //
    public void addGuitar(String serialNumber, double price, Builder fender, String model,
                                                                             Type electric, Wood alder, Wood alder2){
        Guitar guitar = new Guitar(serialNumber, price, fender, model, electric, alder, alder2);
        guitars.add(guitar);
    }
    
    // 시리얼 넘버에 해당되는 기타를 찾음 //
    public Guitar getGuitar(String serialNumber){
        for(Iterator i = guitars.iterator(); i.hasNext(); ){
            Guitar guitar = (Guitar)i.next();
            if(guitar.getSerialNumber().equals(serialNumber)){
                return guitar;
            }
        }
        return null;
    }
    
    // 특정 스펙의 기타를 찾음 //
    public List search(Guitar searchGuitar){
        List machingGuitars = new LinkedList();
        
        for(Iterator i = guitars.iterator(); i.hasNext();){
            Guitar guitar = (Guitar)i.next();
 
            if(searchGuitar.getBuilder() != guitar.getBuilder())
                continue;
            // 모델명은 무수히 많음으로 열거형으로 표현하기 부적절함//
            String model = searchGuitar.getModel().toLowerCase();
            if((model != null) && (model.equals("")) &&
                                                         (!model.equals(guitar.getModel().toLowerCase())))
                continue;
            if(searchGuitar.getType() != guitar.getType())
                continue;
            if(searchGuitar.getBackWood() != guitar.getBackWood())
                continue;
            if(searchGuitar.getTopWood() != guitar.getTopWood())
                continue;
            // 원하는 기타를 찾은 경우 //
            machingGuitars.add(guitar);
        }
        // 기타가 존재하지 않는 경우 //
        return machingGuitars;
    }
}


위와같이 리스트를 만든 후, 원하는 기타를 모두 추가한 후에 최종적으로 리스트를 반환합니다.

메인부분도 조금만 고치면 되겠네요.

다음과 같이 수정해줍니다.


FindGuitarTester.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class FindGuitarTester {
    public static void main(String[] args){
        Inventory inventory = new Inventory();
        initializeInventory(inventory);
        
        Guitar whatJinLikes = new Guitar("", 0, Builder.FENDER, "Stratocastor"
                                                               Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
        
        List machingGuitars = inventory.search(whatJinLikes);
        
        if(!machingGuitars.isEmpty()){
            for(Iterator i = machingGuitars.iterator(); i.hasNext();){
                Guitar guitar = (Guitar)i.next();
                System.out.println(
                "Builder : " + guitar.getBuilder() + ", Mode : "  + guitar.getModel()    + ", " +
                "Type : "    + guitar.getType() + ", BackWood : " + guitar.getBackWood() + ", " +
                "TopWood : " + guitar.getTopWood() + ", Price : " + guitar.getPrice());
            }
        }
        else{
            System.out.println("Sorry, we have nothing for you.");
        }
    }
    private static void initializeInventory(Inventory inventory){
        inventory.addGuitar("V95693", 1499.95, Builder.FENDER, "Stratocastor",
                                                                   Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
        inventory.addGuitar("V9512",  1549.95, Builder.FENDER, "Stratocastor",
                                                                   Type.ELECTRIC, Wood.ALDER, Wood.ALDER);
    }
}



아래와같이 조건에 맞는 두개의 기타를 찾아 출력해주는것을 확인할 수 있습니다.





자, 멋진 소프트웨어를 만들기 위한 첫번째 단계 "먼저, 고객이 원하는 기능을 수행하라."를 충족시켰네요.


이제 다음단계 "객체지향의 기본원리를 적용해서 유연하게 개발하라."를 만족시킬 수 있도록 더 공부해 보겠습니다.



다음 내용 공부하기




posted by 셀로브 2013. 10. 21. 07:14

대소문자가 구분되어진 문자열등을 비교할때에 열거형을 이용하면 많은 오류를 예방할 수 있습니다.
타입의 안전성과 값의 안전성이 증가하기 때문이니다. 아래와같이 열거형을 작성해봅시다.

Builder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public enum Builder {
    FENDER, MARTIN, GIBSON, COLLINGS, OLSON, RYAN, PRS, ANY;
    
    public String toString(){
        switch(this){
        case FENDER:
            return "Fender";
        case MARTIN:
            return "Martin";
        case GIBSON:
            return "Gibson";
        case COLLINGS:
            return "Collings";
        case OLSON:
            return "Olson";
        case RYAN:
            return "Ryan";
        case PRS:
            return "Prs";
        case ANY:
            return "Any";
        default:
            return "";
        }
    }
}



Type.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public enum Type {
    ACOUSTIC, ELECTRIC;
    
    public String toString(){
        switch(this){
        case ACOUSTIC:
            return "acoustic";
        case ELECTRIC:
            return "electric";
        default:
            return "";
        }
    }
}

Wood.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public enum Wood {
    INDIAN_ROSEWOOD, BRAZILIAN_ROSEWOOD, MAHOGANY, MAPLE, COCOBOLO,
 CEDAR, ADIRONDACK, ALDER, SITKA;
    
    public String toString(){
        switch(this){
        case INDIAN_ROSEWOOD :
            return "Indian Rosewood";
        case BRAZILIAN_ROSEWOOD :
            return "Brazilian Rosewood";
        case MAHOGANY :
            return "Mahogany";
        case MAPLE :
            return "Maple";
        case COCOBOLO :
            return "Cocobolo";
        case CEDAR :
            return "Cedar";
        case ADIRONDACK :
            return "Adirondack";
        case ALDER :
            return "Alder";
        case SITKA :
            return "Sitka";
        default:
            return "";
        }
    }
}

이제, FindGuitarTester.java 의 이전의 main함수에서 whatJinLikes객체를 초기화할때 열거형을 사용할 수 있습니다. 열거형 이름 Builder를 입력한후 점('.')을 입력하면 아래와 같이 열거형 Builder의 값 집합들을 볼 수 있습니다.






기존에 문자열비교를 하였던 부분은 다음과같이 바뀌었습니다. 많이 간략해진것을 확인할수 있습니다.




그러나 모델(model)의 경우에는 무수히 많으며 예측하기 어려우므로 열거형으로 표현하기에 부적절합니다. 때문에 모델은 toLowerCase()함수를 통해 소문자로 변경한 후에 문자열비교를 해줍니다.

이제, 고객이 원하는 프로그램을 만들어 나가는데 한발 내딛었네요. 프로그램도 조금 더 견고해 졌습니다. 하지만 처리해야할 다른 요구사항이 있네요.



다음 내용 공부하기