Back Ground

결합도/다형성/디자인 패턴 본문

Spring

결합도/다형성/디자인 패턴

Back 2016. 7. 14. 17:32

결합도(Coupling)가 높은 프로그램


결합도란 하나의 클래스가 다른 클래스와 얼마나 많이 연결되어 있는지를 나타내는 표현이며, 결합도가 높은 프로그램은 유지보수가 어렵다. 이 결합도와 유지보수 관계를 이해하기 위해서 간단한 실습을 진행해보자.


BoardWeb 프로젝트 src/main/java 소스 폴더에 SamsungTV 클래스를 작성한다.



 

결합도가 낮다? 높다?  제공자 (타.이.어)



 back:

결합도가 어떤경우가 높고 낮은건가요?



타.이.어:

스프링 DI가 결합도를 낮추기 위한 기법중 하나인 것 같아요

클래스 하나의 내용을 고치고, 프로그램이 에러없이 그대로 돌아가려면


100개의 다른 클래스를 고쳐야하면 결합도가 높은 것이고

1개의 클래스만 고치면 되는게 결합도가 낮은거에요


결합도가 낮은 프로그래밍일수록 좋은거죠


 
back:
그렇다면 다형성이 이루어져있는게
결합도가 낮은 프로그래밍이겠군요!!

고맙습니다

 




SamsungTV.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
package plymorphism;
 
public class SamsungTV{
 
 public void powerOn(){
     System.out.println("SamsungTV---전원 켠다.");
 } public void powerOff(){
     System.out.println("SamsungTV---전원 끈다.");
 } public void volumeUp(){
     System.out.println("SamsungTV---소리 올린다.");
 } public void volumeDown(){
     System.out.println("SamsungTV---소리 내린다.");
 }
}


SamsungTV 클래스에는 TV 시청에 필요한 필수 기능인 네 개의 메소드가 있다.

그리고 SamsungTV와 같은 기능의 LGTV 클래스도 작성한다.









LGTV.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
package plymorphism;
 
public class LgTV{
 
 public void turnOn(){
     System.out.println("LgTV---전원 켠다.");
 } public void turnOff(){
     System.out.println("LgTV---전원 끈다.");
 } public void soundUp(){
     System.out.println("LgTV---소리 올린다.");
 } public void soundDown(){
     System.out.println("LgTV---소리 내린다.");
 }
}




LgTV클래스에도 SamsungTV 클래스와 같은 기능을 수행하는 메소드가 있지만, SamsungTV의 메소드 이름과 다르다. 이제 이 두 TV 클래스를 번갈아 사용하는 TVUser프로그램을 구현해보자.






TVUser.java


1
2
3
4
5
6
7
8
9
10
11
12
package plymorphism;
 
public class TVUser{
 
 public static void mian(string[] args){
   SamsungTV tv = new SamsungTV();
   tv.powerOn();
   tv.volumeUp();
   tv.volumeDown();
   tv.powerOff();
 }
}



SamsungTV객체를 생성하여 메소드를 호출했으므로 프로그램의 실행 결과는 다음과 같다.



<실행결과>

SamsungTV---전원 켠다.

SamsungTV---소리 올린다.
SamsungTV---소리 린다.
SamsungTV---전원 끈다.




이제 SamsungTV를 시청하는 TVUser 프로그램을 LgTV를 시청하는 프로그램으로 수정해보자.




TVUser.java


1
2
3
4
5
6
7
8
9
10
11
12
package plymorphism;
 
public class TVUser{
 
 public static void mian(string[] args){
   LgTV tv = new LgTV();
   tv.turnOn();
   tv.turnUp();
   tv.turnDown();
   tv.turnOff();
 }
}


SamsungTV와 LgTV는 메소드 시그너처(signature)가 다르므로 TVUser 코드 대부분을 수정해야 TV를 교체할 수 있다. 현재 상태에서는 두 TV 스가 같은 메소드를 가지게끔 강제할 어떤 수단도 없다. 만약 TVUser와 같은 클라이언트 프로그램이 하나가 아니라 여러 개라면 유지보수는 더욱더 힘들것이며, TV 교체를 결정하기 쉽지 않을 것이다.








다형성 이용하기


결합도를 낮추기 위해서 다양한방법을 사용할 수 있겠지마느 가장 쉽게 생각할 수 있는 것이 객체지향 언어의 핵심 개념인 다형성(Polymorphism)을 이용하는 이다. 앞에서 작성한 프로그램을 다형성을 이용하여 수정해보자. 다형성을 이용하려면 상속과 메소드 재정의(Overriding),그리고 형변환이 필요하며, 자바 같은 객체지향 언어는 이를 문법으로 지원한다.





-----------------------------------------------------------------------------------------------------




형성

마지막으로 객체지향의 세번째 특징 다형성은 오버라이딩(재정의)과 상속을 이용하여 실행결과가 다양한 객체를 처리할 수 있는특징을 가지고 있는데요.






일반적으로 알고 있는 A test = new A(); 코드를

위와 같이 A가 아닌 B나 C로도 객체를 받을 수 있도록 하는것이 다형성입니다.




또 한 자바의 모든 클래스는 보이지는 않지만 Object(객체)라는 최상위 부모 클래스를 상속받고 있는데요.

자바의 특성상 다중상속이 안되므로 학교는 학생을 상속받고,

학생은 Object를 상속받기 때문에 모든 클래스는 객체가 됩니다.



그래서 A test = new A();도 되고 A test = new B();도 가능하다는 이론인데,

부족한 글실력과 그림실력으로 표현하기에는 한계가 있는것 같네요.



정리하자면 객체는 어디에 대입할 수 있게 부품화를 시킬수 있습니다. 즉

즉, 부모객체에는 모든 자식 객체가 대입할 수 있다는 말이고,

자식객체에 해당 메소드가 없다면 부모 객체의 메소드가 실행됩니다.


출처 : http://hunit.tistory.com/152






-----------------------------------------------------------------------------------------------------------------







- TVUser와 TV 클래스의 관계 -


TV클래스들을 최상위 부모로 사용할 TV 인터페이스를 추가하고, 모든 TV가 공통으로 가져야 할 메소드들을 추상 메소드을 추상 메소드로 선언한다.








TV.java


1
2
3
4
5
6
7
8
package plymorphism;
 
public interface TV{
   public void powerOn();
   public void powerOff();
   public void volumeUp();
   public void volumeDown();
}


이제 SamsungTV와 LgTV 클래스를 수정하여 방금 추가한 TV 인터페이스를 구현하도록 한다.










samsungTV.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package plymorphism;
 
public class SamsungTV implements TV{
   public void powerOn(){
    System.out.println("SamsungTV---전원 켠다");
   }
   public void powerOff(){
    System.out.println("SamsungTV---전원 끈다");
   }
   public void volumeUp(){
    System.out.println("SamsungTV---소리 울린다");
   }
   public void volumeDown(){
    System.out.println("SamsungTV---소리 내린다");
   }
}



이렇게 하면 SamsungTVLgTV 클래스는 TV 인터페이스에 선언된 추상 메소드들을 모두 재정의해야만 한다.


TV의 인터페이스로

    public void powerOn();   public void powerOff();   public void volumeUp();   public void volumeDown();

를 사용할수 있게 된다.










LgTV.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package plymorphism;
 
public class LgTV implements TV{
   public void powerOn(){
    System.out.println("LgTV---전원 켠다");
   }
   public void powerOff(){
    System.out.println("LgTV---전원 끈다");
   }
   public void volumeUp(){
    System.out.println("LgTV---소리 울린다");
   }
   public void volumeDown(){
    System.out.println("LgTV---소리 내린다");
   }
}



결국, 인터페이스를 이용하여 모든 TV클래스가 같은 메소드들을 가질 수 밖에 없도록 강제 할 수 있게 되었다.

이제 이 두 TV를 이용하는 TVUser클래스를 다음과 같이 수정해보자.








TVUser.java

1
2
3
4
5
6
7
8
9
10
11
package plymorphism;
 
public class TVUser{
 public static void main(String[] args){
    TV tv = new SamsungTV();
    tv.powerOn();
    tv.volumeUp();
    tv.volumeDown();
    tv.powerOff();
 }
}



TVUser클래스는 TV 인터페이스 타입의 변수로 SamsungTV 객체를 참조하고 있다.


이렇게 묵시적 형변환(Promotion)을 이용하여 객체를 참조하면 SamsungTV를 LgTV 객체로 변경할 때,

참조하는 객체만 변경하면 되므로 객체를 쉽게 교체할 수 있다.


이렇게 다형성을 이용하면 TVUser와 클라이언트 프로그램이 여러 개 있더라도 있더라고 최소한의 수정으로 TV를 교체할수 있다.

따라서 유지보수는 좀 더 편해졌다고 볼 수 있다.





디자인 패턴 이용하기


결합도를 낮추기 위한 또 다른 방법으로 디자인 패턴을 이용하는 방법


앞에서 살펴본 다형성을 이용하는 방법은 메소드를 호출할 때 인터 페이스를 이용함으로써 좀 더 쉽게 TV를 교체할 수 있었다.

하지만 이 방법 역시 TV를 변경하고자 할 때, TV클래스 객체를 생성하는 소스를 수정해야만 한다.


TV를 교체할 때, 클라이언트 소스를 수정하지 않고 TV를 교체할 수만 있다면 유지보수는 더욱 편리해질 것이다. 이를 위해서 Factory패턴을 적용해야 하는데, Factory패턴클라이언트에서 사용할 객체 생성을 캡슐화하여 TVUser와 TV사이를 느슨한 결합 상태로 만들어준다.


다음과 같이 Factory패턴이 적용된 BeanFactory클래스를 추가한다.



BeanFactory.java

1
2
3
4
5
6
7
8
9
10
11
12
package polymorphism;
 
public class BeanFactory{
    public Object getBean(String beanName){
        if(beanName.equals("samsung")){
            return new SamsungTV();
        }else if(beanName.equals("lg")){
            return new LgTV();
        }
        return null;
    }
}



-해석-

BeanFactory클래스의 getBean() 메소드는 매개변수로 받은 beanName에 해당하는 객체를 생성하여 리턴한다. 이제 이 BeanFactory 클래스를 이용하여 사용할 TV객체를 획득하도록 TVUser클래스를 수정한다.



TVUser.java

1
2
3
4
5
6
7
8
9
10
11
12
package polymorphism;
 
public class TVUser{
    public static void main(String[] args){
        BeanFactory factory = new BeanFactory();
        TV tv = (TV)factory.getBean(args[0]);
        tv.powerOn();
        tv.volumeUp();
        tv.volumeDown();
        tv.powerOff();
    }
}



-해석-

프로그램을 실행할 때 명령해 매개변수를 전달하지 않으면 다음처럼 예외가 발생한다





매개변수


 

변수들간의 합수적 관계를 설명할 때, 두변수 사이에서 연계하는 변수


 ex) A→B→C    에서  (B가 매개변수)


 A의 효과가 B를 거쳐서 C에 전달 되고 있다. (물론 A가 C에 직접연결 가능)

전체적으로 또는 부분적으로 A의 움직임은 일단 B효과를 마치고,

이어서 B가 C에 미치는 효과가 발생할 때 B는 A와 C사이의 매개변수이다



<실행결과>

Exception in thread "main" java.langArrayIndexOutofBoundsException: 0 at polymorphism.TVUser.main(TVUser.java:7)




-명령행 매개변수 전달하기-


따라서 명령행 매개변수"lg" 나 "samsung"이라는 문자열을 넘겨줘야 한다.

TVUser클래스에서 마우스 오른쪽버튼을 클릭하고 [Run As] → [Run Configurations]를 선택한다.



또는





[Arguments] 탭을 선택하고 "lg" 혹은 "samsung"을 입력하고 <Run> 버튼을 클릭하면 실행된다.



실행되는 TV를 변경하고 싶은 때는 명령행 매개변수만 수정하여 실행한다.

결국 클라이언트 소스를 수정하지 않고도 실행되는 객체를 변경할 수 있다.



<결과>


-TVUser 프로그램 실행 과정 -


이런 결과를 얻을 수 있었던 것은 TV객체를 생성하여 리턴하는 BeanFactory 때문이다.


클라이언트에 해당하는 TVUser는 자신이 필요한 객체를 직접 생성하지 않는다. 만약 그랬다면 TV가 변경될 때마다 소스를 수정해야

했을 것이다. TVUser는 단지 객체가 필요하다는 것을 BeanFactory에 요청했을 뿐이고,

BeanFactroy가 클라이언트가 사용할 TV객체를 적절하게 생성하여 넘겨 준 것 이다.

'Spring' 카테고리의 다른 글

Spring EcoLibrary - Mybatis 순서  (0) 2016.10.12
Spring Framework: annotation 정리 #1  (0) 2016.09.22
Spring 컨테이너 및 설정 파일  (0) 2016.07.19
IoC 컨테이너  (0) 2016.07.15
IoC/AOP/컨테이너  (0) 2016.07.14
Comments