posted by 셀로브 2013. 12. 10. 17:38

레이블링(Labeling)은 인접한 화소끼리 같은 번호를 붙이는 행위를 의미합니다.




전체 픽셀을 검사하여 특정 픽셀을 찾습니다. 목표 픽셀을 찾은 경우 인접한 픽셀에 동일한 조건의 픽셀이 존재하는지 검사하고 같은 번호로 묶어줍니다.

posted by 셀로브 2013. 10. 22. 21:44

1. 단일 책임의 원칙(SRP : Single Responsibility Principle)
- 한 객체는 하나의 책임을 져야 한다는 원칙으로 높은 응집도와 낮은 결합도를 기본으로 하고 있다.

2. 의존 관계 역전의 법칙(DIP : Dependency Inversion Principle)
- 클라이언트는 클래스가 아닌 추상화(인터페이스, 추상클래스) 레이어에 의존해야 한다는 원칙으로, 확장 이슈가 있는 부분은 추상화를 해야 된다는 내용이다.


3. 인터페이스 분리의 원칙(ISP : Interface SegreGation Principle)
- 클라이언트에 특화된 여러개의 인터페이스가 하나의 범용 인터페이스보다 났다.


4. 리스코프 대체 원칙(LSP : Liskov Substitution Principle)
- 상위 클래스는 파생클래스로 대체 가능해야 되는 원칙으로, 기반 클래스의 기능은 파생 클래스가 포함을 해야 된다는 내용이다. 따라서 파생클래스는 상위 클래스보다 더 많은 기능을 제공 하게 되어있다.


5. 개방 폐쇄 원칙(Open-Closed Principle)
-확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다는 원칙으로 기존의 클래스에 수정하지 말고, 상속 또는 구현으로 확장을 해야 된다는 내용이다.


출처 : http://www.sjava.net/13

posted by 셀로브 2013. 10. 22. 04:32


ricksGuitars-encapsulation.zip



설계가 잘 되었는지 확인하고 살펴보는 단계입니다.


이제 "3) 유지보수와 재사용이 쉬운 디자인을 위해 노력하세요" 에대한 단계입니다.


새로운 기능의 추가를 원할 때 더 쉽게 확장할수 있게하고, 프로그램의 일부를 재사용이 쉽도록 하는 과정입니다.


Guitar의 내용을 간단한 Guitar객체와 GuitarSpec객체로 분리하여 구조화했지만 향후 유지보수를 위해 이과정을 거쳐야 합니다.




GuitarSpec에 기타 줄의 갯수에 대한 속성을 추가해달라는 요구가 왔을 경우를 생각해 봅시다.


Guitar클래스의 생성자의 매개변수를 추가해 주어야 하며, Inventory클래스의 search()메소드에 속성비교를 하나 추가해야 합니다.


GuitarSpec이 변경되었을 뿐인데, Guitar클래스와 Inventory클래스까지 변경해야 된다는 것입니다.


Guitar클래스의 생성자에서 GuitarSpec의 생성역할까지 맡았기 때문입니다.


또한, Inventory의 search()함수는 검색기능뿐만아니라, 스펙을 비교하는 기능까지 수행하고 있기 때문입니다.





해결방법은 다음과 같습니다.

1. Guitar클래스의 생성자에서 GuitarSpec의 속성을 생성자로부터 캡슐화하여 분리해야 합니다.


FindGuitarTester.java



Guitar.java



이제 GuitarSpec클래스에 새로운 속성이 추가되어도, Guitar클래스에는 변경될 사항이 없습니다.



2. search()메소드를 수정하여 GuitarSpec클래스에 두개의 GuitarSpec객체를 비교하도록 위임 합니다.


Inventory.java




GuitarSpec.java



이처럼 search()함수에서는 검색기능만 수행할 뿐, GuitarSpec객체의 비교는 GuitarSpec클래스의 maches()함수에 위임한것을 확인할 수 있습니다.

이제, 다른 속성이 추가되더라도, GuitarSpec클래스의 maches()함수에 조건문 하나만 추가되면 됩니다.



ricksGuitars-final.zip





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()함수를 통해 소문자로 변경한 후에 문자열비교를 해줍니다.

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



다음 내용 공부하기



posted by 셀로브 2013. 10. 21. 05:41

ricksGuitars-start.zip


Head First Object-Oriented Analysis & Design 책을 학습하며 공부한 내용을 적을 예정입니다.

해당 책의 소스코드는 http://www.headfirstlabs.com에서 제공한다고 합니다.

1장의 내용을보니 3가지 단계로 멋진 소프트웨어를 만들 수 있다고 합니다. 정리해보면 다음과 같습니다.

1) 먼저, 고객이 원하는 기능을 수행하라.
2) 객체지향의 기본원리를 적용해서 유연하게 개발하라.
3) 유지보수와 재사용이 쉬운 디자인을위해 노력하라.


다음은 기타를 등록하거나 고객이 원하는 기타를 제공하는 예제입니다.
이는 아직 불완전한 소스코드이며, 하나씩 고쳐가면서 객체지향 공부를 진행해 보겠습니다.

그전에 클래스 다이어그램입니다.
아직 간단한 구조이지만 소스코드를 보기전에 클래스 다이어그램을 접하면 접근이 더욱 쉽습니다.



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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.tistory.shelob;
 
public class Guitar {
    private String serialNumber, builder, model, type, backWood, topWood;
    private double price;
    
    public Guitar(String serialNumber, double price, String builder, String model, String type,
    String backWood, String topWood){
        this.serialNumber = serialNumber;
        this.price = price;
        this.builder = builder;
        this.model = model;
        this.type = type;
        this.backWood = backWood;
        this.topWood = topWood;
    }
    
    public String getSerialNumber(){
        return serialNumber;
    }
 
    public double getPrice() {
        return price;
    }
    
    public void setPrice(float newPrice){
        this.price = newPrice;
    }
    
    public String getBuilder() {
        return builder;
    }
 
    public String getModel() {
        return model;
    }
 
    public String getType() {
        return type;
    }
 
    public String getBackWood() {
        return backWood;
    }
 
    public String 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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.tistory.shelob;
 
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
 
public class Inventory {
    private List guitars;
    
    public Inventory(){
        guitars = new LinkedList();
    }
 
    // 새로운 기타를 생성하고 목록에 등록 //
    public void addGuitar(String serialNumber, double price, String builder, String model,
     String type, String backWood, String 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 Guitar search(Guitar searchGuitar){
        for(Iterator i = guitars.iterator(); i.hasNext();){
            Guitar guitar = (Guitar)i.next();
 
            // "", 0, "fender", "Stratocastor", "electric", "Alder", "Alder"
            
            String builder = searchGuitar.getBuilder();
            if((builder != null) && (!builder.equals("")) && (!builder.equals(guitar.getBuilder())))
                continue;
            String model = searchGuitar.getModel();
            if((model != null) && (!model.equals("")) && (!model.equals(guitar.getModel())))
                continue;
            String type = searchGuitar.getType();
            if((type != null) && (!type.equals("")) && (!type.equals(guitar.getType())))
                continue;
            String backWood = searchGuitar.getBackWood();
            if((backWood != null) && (!backWood.equals("")) &&
                            (!backWood.equals(guitar.getBackWood())))
                continue;
            String topWood = searchGuitar.getTopWood();
            if((topWood != null) && (!topWood.equals("")) &&
                            (!topWood.equals(guitar.getTopWood())))
                continue;
            
            // 원하는 기타를 찾은 경우 //
            return guitar;
        }
        // 기타가 존재하지 않는 경우 //
        return null;
    }
}
 



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
package com.tistory.shelob;
 
public class FindGuitarTester {
    public static void main(String[] args){
        Inventory inventory = new Inventory();
        initializeInventory(inventory);
        
        Guitar whatJinLikes = new Guitar("", 0, "fender""Stratocastor""electric""Alder""Alder");
        
        Guitar guitar = inventory.search(whatJinLikes);
        if (guitar != null){
            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, "Fender""Stratocastor""electiric""Alder""Alder");
    }
}
 


FindGuitarTester.java의 20번째 줄에서 기타를 등록했고, 그 기타와 동일한 기타를 10번째 줄에서 요구하고

있습니다. 그러나, 위 소스는 항상 "Sorry, we have nothing for you."라는 말로 기타가 없다는 출력을 내보냅니다. 분명 기타를 입력한것 같은데 말이지요.


8번째줄의 기타의 타입은 "fender"이고, 21번째 기타의 타입은 "Fender"이기 때문에 대소문자의 차이로 해당 기타가 없다고 출력했던 것입니다.


위에서 언급한 3가지 단계중 "1) 먼저, 고객이 원하는 기능을 수행하라."처럼 먼저 고객이 요청한 내용에 좀더 바른 기능을 수행하도록 변경하도록 하겠습니다.





다음 내용 공부하기