1장 Strategy Pattern (끝)

최종적으로 위와 같은 다이어그램이 완성됩니다.
상속으로 시작했던 디자인이 composition(구성)을 사용함으로써 결말이 났군요.
이로써 다음과 같은 디자인 원칙을 배울 수 있습니다.

디자인 원칙3
상속보다는 구성을 활용한다.

위에서 했던 행동들이 하나의 디자인 패턴으로 정립되어 있었습니다.
바로 Strategy pattern 이라는 것으로 써 정의는 다음과 같습니다.
Strategy Pattern에서는 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있돌고 만든다. 이것을 사용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.
위키피디아 에서 정의한 내용은 다음과 같습니다.

Strategy pattern

From Wikipedia, the free encyclopedia

Jump to: navigation, search

In computer programming, the strategy pattern is a particular software design pattern, whereby algorithms can be selected on-the-fly at runtime.
In some programming languages, such as those without polymorphism, the issues addressed by this pattern are handled through forms of reflection, such as the native function pointer or function delegate syntax.
The strategy pattern is useful for situations where it is necessary to dynamically swap the algorithms used in an application. The strategy pattern is intended to provide a means to define a family of algorithms, encapsulate each one as an object, and make them interchangeable. The strategy pattern lets the algorithms vary independently from clients that use them.
runtime시에 알고리즘을 선택할 수 있는 특징이 있고 다형성이 지원되지 않는 언어의 경우 포인터와 delegate syntax와 같은 reflection의 형태로 다뤄집며 유용한 상황으로는 동적으로 알고리즘을 바꾸고 싶을 경우라고 간략히 요약할 수 있습니다.
원문은 위의 링크와 같으며 예제로 여러가지 sorting 알고리즘을 동적으로 바꿔가며 사용해보는 예제가 C++언어로 나와있습니다.
더 구체적인 내용은 이곳에서 볼 수 있습니다.
decoupling에 관한 내용과 여러 장점에 대해 설명이 되어 있습니다.
다음은 기본 Strategy Pattern의 기본 UML입니다.
마지막으로 실생활에 Strategy Pattren을 적용한 예를 살펴보겠습니다.


1장 Strategy Pattern(계속)

이전 글에서 발생했던 문제를 해결하기 위해서 interface를 사용하여 날아다닐 수 있는 오리와 소리낼 수 있는 오리로 구분하였습니다.
날아 다니거나 소리 낼 수 있는 오리들은 해당 interface를 구현해야 하며 따라서 fly나 quack과 같은 method들을 구현해 주어야 합니다.
만약에 이런 상황에서 오리의 소리가 공통적으로 바뀐다면 어떻게 해야할까요? 아마 Quackable을 구현한 모든 class들을 수정해야 할 것입니다.
(현재는 class들의 수가 몇개 안되지만 50개 라고 하면 50번을 수정해야 합니다.)
좀더 좋은 방법이 필요할 듯합니다.
지금까지 계속 변화를 가정하여 class들이 보다 유연하게 구성되도록 진행되가고 있습니다.(그만큼 변화가 빈번히 일어난다는 것이겠죠.)
이러한 변화 속에서 살아남으려면 다음과 같은 디자인 원칙에 유의해야합니다.

디자인원칙1
애플리케이션에서 달라지는 부분을 찾아 내고, 달라지지 않는 부분으로부터 분리시킨다.

즉 fly()와 quack()은 Duck class에서 오리마다 달라지는 부분입니다.
이 두 메소드를 Duck에서 뽑아 내어 새로운 class집합으로 만들 것입니다.
이 class집합들은 다음의 디자인 원칙에 따라 구현합니다.

디자인원칙2
구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.

이 말은 선뜻 이해가 되지 않습니다.
여기서 구현이라고 하는 것은 특정 class에 있는 method를 말하는 것이고 인터페이스는 상위 classl에 있는 method들을 말한다고 생각합니다.
즉.. Animal a = new Dog(); or new Cat();
a.eat();
을 실행했을 때 개도 먹일 수 있고 고양이도 먹일 수 있도록 프로그래밍 하라는 뜻으로 이해가 됩니다.
(저 방법 보다 Animal a = getAnimal(); 이런 방법으로 new 키워드를 없애는 것이 보다 더 디자인원칙2를 잘 지키는 것이라고 나와있습니다.)
따라서 다음과 같은 다이어 그램으로 class집합을 구현할 수 있습니다.
이제 Duck class에서는 위 두 집합의 상위Type의 변수를 가진다면 a.eat()과 같은 장점(다형성)을 누릴 수 있습니다.
위와 같이 FlyBehavior와 QuackBehavior type의 변수를 가지고 있고
performFly()와 performQuack()에서는
flyBehavior.fly()와 quackBehavior.quack()를 실행하기만 하면 됩니다.
이렇게 해두면 각각의 오리( sub class )들은 dispaly()만 구현하면 되고 자기 자신의 quack이나 fly의 Behavior가 어떤 것인지 설정해 줌에 따라 결과가 달라지게 됩니다.
자신의 Behavior를 설정하는 방법은 객체 생성시에 생성자에서 하는 방법과
나중에 setter메소드를 사용하는 방법이 있습니다.
setter메소드를 이용하여 Behavior를 지정해 줄 경우 날수 없던 오리(FlyNoWay Behavior로 setting했던 오리)가 갑자기 날개를 가지고 날아갈 수있게(FlyWithWings Behavior로 오리의 Behavior를 프로그램 실행도중 setter method를 사용하여 setting하면 됩니다.) 마술을 부릴 수도 있습니다.

1장 Strategy Pattern

위 다이어그램을 보면 Duck class를 상속을 이용하여 재사용 하고 있습니다.
필요한 부분( 여기서는 display() )만 overriding 해주면 되기 때문에 상당히 편리해 보입니다.
기본 기능으로 오리가 날수 있도록 하고 싶을 때…
위와 같이 상위 class에 추가하기만 하면 모든 하위 class들에 일일히 추가할 필요가 없기때문에 편리해 보입니다.
하지만…
날지 못하는 오리(고무 인형 오리)의 경우라면 상속 받지 말아야 할텐데.
특정 method를 제외하고 상속하는 방법 같은 것은 배운적이 없네요 🙂
상속을 못하게 하는 방법은 있지만 그렇게 하면 다른 자식 class들은 어떻게 하나요? 하핫;
method하나가지고 뭘 그러나.. 그냥 method를 overring해서 안에서 아무것도 안하면 되지 않을까…라는 생각을 처음엔 했었습니다.
그러나…
그러한 method가 무진장 많다고 했을 때를 생각해 보면 코드 낭비에 시간 낭비라는 생각이 안들 수가 없습니다. 무언가 뾰족한 수가 필요합니다.

Traditions of language oriented programming

  • You, We.

굳이 당신, 여러분, 우리, 나… 이런 말을 넣지 않아도 된다. 어색하다.

  • class와 같이 다양한 의미로 사용가능한 단어.

명확한 의미로 번역하자.

  • These files are essentially DSLs.

이 파일들 역시 본질적으로 DSL이다.

  • they allow people familiar with the adaptive model to be extremely productive once the model is developed and shaken down.

이러한 모델은 한번 개발되어서 자리잡게 되면 개발자들이 모델에 익숙해져서 고도로 생산성 향상을 가져온다.

http://martinfowler.com/articles/languageWorkbench.html#AdaptiveObjectModels
위 글 번역 중 얻은 조언.

제대로된 번역은 언어지향 프로그래밍의 전통 🙂

Lesson 1. Getting Started

Lesson 1. Getting Started

1장에서 배울 것은 :

  • 간단한 Java class 만들기
  • Java class를 가지고 노는 test class 만들기
  • JUnit famework 사용하기
  • 생성자에 대해 배우기
  • 위에서 작성한 code 리팩토링하기.


Testing

Test-driven development means that you will write tests for virtually every bit of code. It also means that you will write the tests first. The tests are a means of specifying what the code needs to do. After writing the corresponding code, the tests are run to ensure that the code does what the tests specify.

The production classes you build should know nothing about the tests written for them.

Design

You start by building only high-level designs, not highly detailed specifications. You will continually refine the design as you understand more about the customer needs. You will also update the design as you discover what works well and what doesn’t work well in the Java code that you build. The power of object-oriented development can allow you this flexibility, the ability to quickly adapt your design to changing conditions.

A Simple Test

You must designate the class as public in order for the testing framework JUnit to recognize it.

The classpath is a list of locations separated by semicolons under Windows or colons under Unix. You supply the classpath to both the compiler and the Java VM. A location can be either a JAR file (which contains compiled class files by definition) or a directory that contains compiled class files.

ex) javac -classpath c:\junit3.8.1\junit.jar StudentTest.java(the abbreviated keyword -cp.)

JUnit

Not only does the Java compiler need to know where the JUnit classes are, but the Java VM also needs to be able to find these classes at runtime so it can load them up as needed.

java -cp .;c:\junit3.8.1\junit.jar junit.awtui.TestRunner StudentTest


Adding a Test

public class StudentTest extends junit.framework.TestCase {
public void testCreate() {
}
}
  • the method must be declared public,

  • the method must return void (nothing),

  • the name of the method must start with the word test, in lowercase letters, and

  • the method cannot take any arguments ().

Creating a Student

new Student(“Jane Doe”);

You terminate each statement with a semicolon (;).

You place the new keyword before the name of the class to instantiate.

String literals represent object instances of the predefined Java class java.lang.String.

When the Java VM executes the new operator in this statement, it allocates an area in memory to store a representation of the Student object. The VM uses information in the Student class definition to determine just how much memory to allocate.

Creating the Student Class

class Student {}

Constructors

A constructor looks a lot like a method. It can contain any number of statements and can take any number of arguments like a method. However, you must always name a constructor the same as the class in which it is defined. Also, you never provide a return value for a constructor, not even void.

Local Variables

Student student = new Student(“Jane Doe”);

When the Java VM executes this statement, it executes the code to the right-hand side of the assignment operator (=) first, creating a Student object in memory. The VM takes note of the actual memory address where it places the new Student object. Subsequently, the VM assigns this address to a reference on the left-hand side, or first half, of the statement.

Returning a Value from a Method

String studentName = student.getName();

you are sending a message to the Student object, using the student reference assigned to in the previous statement.

String getName() { }

This getName method specifies instead a return type of String.

return “”;

The return statement here returns an empty String objecta String with nothing in it.

Refactoring

The first step is to eliminate the unnecessary local variables.
The second step: It is considered poor programming practice to embed String literals throughout your code. One reason is that the code can be difficult to follow if it is not clear what each String literal represents.

this

There are two ways to ensure that the value of the formal parameter is assigned to the field:
The first approach means you must rename either the parameter or the field.
The second approach for disambiguating the two is to use the same name for both, but where necessary refer to the field by prefixing it with the Java keyword this.