객체 지향 원리 송치형
객체 지향 프로그래밍 객체(클래스)들에게 적절히 책임(responsibility)을 분배한 뒤 객체들 간의 의존 관계를 관리(dependency management)하는 작업 객체 지향 원리는 책임 분배와 의존 관계 관리의 가이드 라인 역할
나쁜 디자인의 징후 Rigidity – 변경하기 어렵다. Fragility – 깨지기 쉽다. Immobility – 재사용하기 어렵다. Viscosity – 올바른 일을 하기 어렵다. Needless Complexity – 과도한 디자인 Needless Repetition – Copy&Paste?
단일 책임의 원칙 SRP – Single Responsibility Principle 클래스는 하나의 책임만을 맡아야 한다. ‘책임(responsibility)’ == ‘변경의 원인(reason to change)’ “클래스 변경의 원인은 하나이어야 한다.”
전형적인 예
ActiveRecord와 DataMapper 패턴 DAO 하이버네이트의 Session iBatis의 SqlMapClient HibernateTemplate SqlMapClientTemplate
나쁜 냄새 – 중복 코드 중복 코드는 만악의 근원 DRY : Don’t Repeat Yourself!
나쁜 냄새 – 산탄총 수술 Shotgun surgery 수정을 하려 보니 여러 클래스를 수정해야 하더라. 하나의 책임이 여기저기 흩어져 있는 경우
나쁜 냄새 – 여러 원인에 의한 변경 Divergent Change 한 클래스가 이런 저런 이유로 자주 수정되어야 하더라. 여러 개의 책임을 맡고 있음
Good Example - 레이어
핵심 리팩토링 기법과 책임 분배 Extract Class(6번) Move Method(6번) Extract Method(4번) Move Field(4번) Rename *(N번)
의존 관계 역전의 법칙 DIP : Dependency Injection Principle 인터페이스에 의한 프로그래밍 구체 클래스에 의존하게 되면 변경이 어렵고 재사용성이 떨어진다. Depend on abstractions instead of details
전형적인 예 V.
스프링 프레임워크에서는?
Abstract Factory 패턴 interface Collection { Iterator iterator(); // . . . } interface Iterator { boolean hasNext(); Object next(); class Tree implements Collection { public Iterator iterator() { return new Iterator() { // Tree를탐색할 Iterator 인터페이스를 이곳에 구현 class User // 인터페이스만 사용한다. { public void operation( Collection c ) { Iterator i = c.iterator(); while( i.hasNext() ) doSomethingWith( i.next() ); }
Abstract Factory 패턴 Class.forName(“com.mysql.jdbc.Driver”).newInstance(); Connection con = DriverManager.getConnection(jdbc_url, id, password); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); public JDBCDriver(){ try{ java.sql.DriverManager.registerDriver(new JDBCDriver()); }catch(SQLException e){ } } public boolean acceptsURL(String url) throws SQLException{ return url.startWith(“jdbc:postgresql??); } Abstract Factory 패턴과 Chain of Responsibility 패턴을 함께 사용한 예
인터페이스 분리의 원칙 ISP : Interface Segregation Principle Split Interface to manage dependency. Clients should not be forced to depend on methods that they do not use.
전형적인 예
ATM UI 예제
분리된 ATM UI 예제
스프링의 ApplicationContext
(cf) IoC, 헐리우드 원칙
리스코프 대체 원칙 LSP : Liskov Substitution Principle 기반 타입은 서브 타입으로 대체할 수 있어야 한다.
하이버네이트 버전 업그레이드 … Panic! 패키지 구조 변화 Session 인터페이스의 변화 그리고, HQL 파서의 변화 net.sf.* org.hibernate.* Session 인터페이스의 변화 그리고, HQL 파서의 변화 … Panic!
이렇게 하면? class InfoHelper{ public static java.util.List addInfo(java.util.List currentInfo){ String info = “new info”; currentList.add(info); return currentInfo; } String[] infoValues = new String[]{“info1”, “info2”, “info3”}; List infoList = Arrays.asList(infoValues); infoList = InfoHelper.addInfo(infoList); Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:150) …
예외 없는 규칙은 없다. 상속 구조의 폭주와 Decorator 패턴 void f(){ Collection collection = new HashSet(); // … modify(collection); } void modify(Collection collection){ collection.add(…); doSomethingWith(collectoin); Collection list = new LinkedList(); list = Collections.unmodifiableCollection(list);
Decorator 패턴 public class Collections{ public static Collection unmodifiableCollection(final Collection wrapped){ return new UnmodifiableCollection(wrapped); } private static class UnmodifiableCollection implements Collection{ private Collection unmodifiable; public UnmodifiableCollection(Collection modifiable){ this.unmodifiable = modifiable; } public boolean contains(){ return unmodifiable.contains(); public void remove(Object obj){ throw UnSupportedOperationException(); // 콜렉션을 조회하는 메소드는 contains와 같은 래퍼 메소드 구현하고, // 콜렉션을 변경하는 메소드는 예외를 던진다. }
개방-폐쇄 원칙 OCP : Open-Closed Principle Extend function without editing code
전형적인 예
Log4j Appender와 Layout
Strategy 패턴 알고리즘 패밀리를 정의하고 각 알고리즘을 캡슐화하여 서로 바꿔 쓸 수 있도록 한다. 전략패턴은 클라이언트와 상관없이 알고리즘을 바꿀 수 있게 해준다.
State 디자인 패턴 객체 내부 상태가 바뀌면 행동 방식을 바꾸고 싶다. 이때 마치 객체들이 클래스가 바뀌는 것처럼 보인다.
빅 리팩토링 SRP LSP OCP SRP DIP