달력

42024  이전 다음

  • 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

개요

object생성을 위한 interface를 정의,해당 interface의 상속class의 FactoryMethodPattern(object를 생성후 반환하는 메소드)가 구체적인 object생성을 맡음.

생성할 object의 종류마다 상속class를 따로 만듦.

사용 

application-specific한 object정보를 분리하여 볼 수 있다. 생성처리 부분이 간단해 지고 새로운 종류의 object 생성기를 추가할 시 기존 생성처리클래스는 고치지 않고 상속하여 처리할 수 있다. 반드시 interface를 상속하여 구현해야 한다는 점이 있으므로, 상속해서 처리하는 게 좋다고 생각할 때 사용함

 

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

퍼옴...http://network.hanbitbook.co.kr/view_news.htm?serial=719

 

저자: 김대곤

 

Abstraction

 

필자는 패턴을 단순화(또는 추상화 Abstraction)시켜서 이해한다고 말했다. Gang of Four의 Design Patterns에는 "Consequences", "Implementation"라는 소제목들이 등장한다. 하지만 필자는 거의 읽어본 적이 없고, 관심도 없다. 그러므로 필자에게 패턴이라는 단어와 "Consequences"나 "Implementation"이라는 단어와 연관되지 않는다. 이렇듯이 관심사항이 아닌 것을 제거해버리는 것을 추상화(Abstraction)이라고 할 수 있다.

 

Factory Method 패턴이 Object가 생성되어야 하는 시점은 알고 있으나 어떤 Object가 생성되어야 하는지 알 수 없을 때, 객체의 생성을 Subclass에 위임하는 방식으로 문제를 해결할 수 있다고 말하고 있다. 첫 번째 질문은 왜 이런 상황에 직면하게 되었는가이다. 어떻게 이런 일이 발생할 수 있는가? 그것은 여러 종류의 객체를 추상화(Abstraction)시켜서 사용했기 때문이다.

 

게임의 예를 들어보자. 이 게임은 병사와 탱크를 가지고 전투를 하는 게임이고, 병사와 탱크를 만들기 위해서 병사를 만들어 내는 막사와 탱크를 만들어 내는 공장이 있어야 된다. 게임의 기능은 "생성", "공격", "이동"이 있다. 생성의 기능을 자세히 살펴보자. 게임상에 존재하는 어떤 물체를 선택하고 "생성" 버튼을 눌렀다고 가정하자. 먼저 선택한 물체가 생성이라는 기능을 가지고 있는 객체인지를 확인하고, 막사이면 병사를 공장이면 탱크를 만들어야 한다. 만약, 실제 객체(막사와 공장)을 사용하였다고 가정하자. 그런데 비행기를 만드는 새로운 공장이 생기면 생성 기능은 수정되어야 한다. 선택한 객체가 새로운 공장인지를 체크해야 하고, 새로운 공장이면 비행기를 만들어야 하기 때문이다. "생성"이라는 기능의 입장에서는 그게 막사인지, 공장인지, 아니면 새로운 공장인지는 관심의 대상이 아니다. "생성" 기능의 입장에선 오직 선택한 객체가 "생성"이라는 기능을 가지고 있는가만 알면 된다. 실제로는 존재하진 않지만 생성이라는 기능을 가진 것들을 Factory라는 개념으로 추상화하여 사용하면, 빈번한 수정과 High Coupling을 피할 수 있다. 객체의 종류에 따라 행동양식이 바뀌는 경우, 조건문을 사용하지 말고 다형성(Polymorphism)를 사용하라는 GRASP 패턴의 "Polymorphism" 원칙을 적용하여 Interface를 만들어서 생성 기능안에서는 Factory Interface만 사용하고 실제 객체들은 Factory Interface 타입의 변수 안에서 모두 숨겨진다. 새로운 공장이 만들 때 Factory Interface만 구현하면 "생성"버튼의 코드는 전혀 수정될 필요가 없다.

"생성" 기능 버튼을 사용자가 클릭하면, 객체를 생성해야 한다. 시점은 알지만 어떤 객체가 생성될 것인지는 선택되는 객체에 따라서 다르다. 그러면 당연히 Factory 객체는 객체의 생성을 담당하는 메소드(병사, 탱크, 비행기 등을 만들어야 함으로)를 가지고 있어야 한다. 이것이 Factory Method 패턴이 직면하는 상황인 것이다. 사실 이 문제는 객체의 생성 뿐 아니라 일반적인 행동(메소드)도 마찬가지이다. "이동" 기능에 대해서 생각해보라.

 

객체 생성을 (생성자가 아닌) 메소드를 통해서 하고자 하는 이유

 

실제 코드 예제를 보기 전에 객체를 생성자가 아닌 메소드를 통해서 생성하려고 하는 이유에 대해서 살펴보자. 메소드를 통해서 생성한다는 것은 Singleton 패턴에 나오는 Singleton 클래스 또는 Static 메소드를 통한 자기 자신 타입의 객체 생성이 아닌 경우에는 객체를 자기 자신의 메소드가 아닌 다른 객체의 메소드 안에서 생성한다는 의미이다. GRASP 패턴에 나오는 Creator 패턴에서 설명했듯이 이런 경우 컨텍스에서 제공받아야 하거나 제약사항들을 체크할 수 있다. 어떤 객체가 Interface를 구현한 객체의 경우, Interface를 쓴 이유는 실제 객체를 숨기기 위해서 한 작업인데, 생성자를 통해서 하면 실제 객체가 드러나 목표하는 효과를 볼 수 없게 되기 때문이다.

너무나 유명하고, 흔한 예제

필자가 아는 선배는 글을 쓰거나 프리젠테이션을 할 때는 항상 예제를 제공하는 것이 기본적인 예의이지 원칙이라고 했다. 설명을 하면 어려운 것도 예제를 보면 쉽게 이해가 되기 때문이겠지만 사실 이해하기 쉬운 예제를 제공하는 것은 쉬운 일이 아니다. Factory Method 패턴은 너무 유명하고 흔해서 검색엔진으로 검색하면 금방 찾을 수 있다. 솔직히 말하면, 필자도 이 예제를 그런 방식으로 구했다. 1분 전에.

 

import java.sql.*;                               

public class JDBCExample {
  private static String url    = "";
  private static String user   = "";
  private static String passwd = "";

  public static void main(String args[]) {
    try {
      Class.forName("oracle.jdbc.driver.OracleDriver");
      
      Connection connection = DriverManager.getConnection(url,user,passwd);
      Statement statement = connection.createStatement();
      ResultSet resultSet = statement.executeQuery("Select to_char(sysdate) from dual");

      while (resultSet.next())
      {
        System.out.println("It is " + resultSet.getString(1)); 
      }

      resultSet.close();
      statement.close();
      connection.close();           
    } catch (Exception e) {
    } finally {
    }
  }
}

몇 가지이 수정이 가해지지 않으면, 이 예제는 실행되지는 않을 것이다. 그러나 Factory Method 패턴을 사용한 클래스가 무려 2개나 된다. 그것도 연결해서 사용했다. Connection 객체는 createStatement() 메소드를 통해서 Statement 객체를 생성하고 Statement 객체는 executeQuery() 메소드를 통해서 ResultSet 객체를 생성하고 있다. Connection, Statement는 모두 Interface이다. 만약 실제 객체를 사용했다면 위의 코드는 다음과 비슷한 모습이 될 것이다.

public class OracleJDBC {
 
  private static String   url    = "";
  private static String   user   = "";
  private static String   passwd = "";

  public static void main(String args[]) {
    try {
      Class.forName("oracle.jdbc.driver.OracleDriver");
      
      OracleConnection connection  = new OracleConnection(url,user,passwd);
      OracleStatement  statement   = new OracleStatement();
      OracleResultSet  resultSet   = statement.executeQuery("Select to_char(sysdate) from dual");

      while (resultSet.next())
      {
        System.out.println("It is " + resultSet.getString(1)); 
      }

      resultSet.close();
      statement.close();
      connection.close();           
    } catch (Exception e) {
    } finally {
    }
  }
}


위 코드는 실행되거나 컴파일이 되거나 하지는 않는다. 실제 오라클을 사용할 경우 쓰이는 객체들이지만 이렇게 코딩하는 사람은 없다. 만약 오라클 안쓰면 다 고쳐야 되는데 누가 이렇게 쓰겠는가? 위의 작업을 하는 사람들에겐 실제 무슨 객체가 쓰이는가는 관심사항이 아니다. 단지 어느 곳에 저장되어 있는 데이터를 읽어서 가져오는 것이 주요 목표인 것이다. JDBC는 각 데이터베이스들을 추상화했지만 더 높은 추상화를 적용한 JDO(Java Data Object)도 있다. JDO는 Persistence에 대한 추상화를 시도하고 있다.

 

결어

 

그럼 추상화(Abstraction)를 어떻게 할 것인가? 이런 문제는 아무도 설명해 주지 않는다. 필자도 누구에게서 추상화의 원리나 관련된 강의를 받은 적이 없다. 필자가 항상 염두에 두는 생각이 있다면 "내가 필요로 하는 최소한의 것만 받아들이겠다"는 자세이다. 누가 나에게 무언가를 요청한다면 "네가 좀 알아서 해 주면 안돼?"라고 말하는 자세 말이다.(실제로 이렇게 살면 맞아 죽겠지만 말이다.) 그렇게 해서 남은 최소한 것을 Interface로 정의해서 사용하자.

필자가 쓰고 있는 패턴에 관한 기사도 Design Pattern를 설명한다기 보다는 Design Pattern이라는 주제에 대해 버리고 버려서 남은, 비슷해 보이지만 전혀 다른 내용이 아닐까 생각한다. 필자는 상속을 좋아하지 않는다. 그래서 SubClass, SuperClass와 같은 용어보다는 Interface라는 용어를 많이 사용한다. 그러나 상속에 대해서도 적용될 수 있음을 밝혀두는 바이다.

 

'JAVA > JEE' 카테고리의 다른 글

웹로직 컴파일 명령  (0) 2004.03.24
[펌] [퍼옴]J2EE와 애플리케이션개발속의 디자인 패턴  (0) 2004.03.19
[펌] MVC모델  (0) 2004.03.19
[펌] 배치디스크립터  (0) 2004.03.19
[펌] UML산출물간의 연관성  (0) 2004.03.19
Posted by tornado
|