Arrays and Hashes

Arrays and Hashes

Ruby의 array와 hash는 인덱스가 있는 collection입니다. 둘 다 객체를 담아 두고 key를 사용하여 접근하는 콜렉션입니다. array에서 key는 정수이지만 hash는 어떤 객체도 key가 될 수 있습니다. 둘 모두 새로운 요소를 추가할 때 커기제 됩니다. array에 접근을 하는게 보다 효율 적이지만 hash는 보다 유연함을 제공합니다. 어떤 array이나 hash들도 여러 타입의 객체를 담아 둘 수 있습니다. 이 말은 하나의 array에 정수, 문자열, 실수를 담을 수 있다는 것입니다.

[ ] 이 괄호 사이에 요소들을 나열 하는 array literal을 사용하여 새로운 배열을 생성하고 초기화 할 수 있습니다. array 객체를 가지고 객체에 있는 각각의 요소들에 [] 안에 index를 사용하여 접근할 수 있습니다. 다음의 예에서 봅시다.

a = [ 1, 'cat', 3.14 ]   # array with three elements
# access the first element
a[0] » 1
# set the third element
a[2] = nil
# dump out the array
a » [1, "cat", nil]

array 객체의 생성자를 사용하여 생성하거나 요소들이 없는 비어있는 괄호 [] 를 사용하여 배열을 생성할 수도 있습니다. Array.new .

empty1 = []
empty2 = Array.new

단어들의 배열을 생성할 때 “” 와 , 를 사용하기가 매우 번거로울 수 있는데 이 때 사용하기 편한 것이 있습니다. 바로 %w 입니다. 다음과 같이 사용할 수 있습니다.

a = %w{ ant bee cat dog elk }
a[0] » "ant"
a[3] » "dog"

Ruby의 hash는 배열과 비슷합니다. hash는 [] 말고 {} 이 괄호를 사용합니다. 반드시 하나의 요소에는 두 개의 객체가 제공되어야 합니다. 하나는 key 하나는 value입니다.

예를들어, 악기들을 오케스크라 위치에 따라 매핑하고 싶다면 다음과 같이 할 수 있습니다.

instSection = {  'cello'     => 'string',  
'clarinet'  => 'woodwind',  
'drum'      => 'percussion',  
'oboe'      => 'woodwind',  
'trumpet'   => 'brass',  
'violin'    => 'string'}

Hash는 array와 같이 [] 괄호를 사용하여 index화 됩니다.

instSection['oboe'] » "woodwind"
instSection['cello'] » "string"
instSection['bassoon'] » nil

마지막 예가 보여주듯이 hash에 해당하는 key가 없는 경우에 기본적으로 nil을 반환합니다. 하지만 가끔은 여러분이 원하는 기본값을 반환하도록 하고 싶을 것입니다. 예를 들어 해당하는 key가 몇 번 출현하는지 카운팅하는 hash의 경우 기본값을 0으로 하고 싶을 것입니다. 이것은 hash를 생성할 때 생성자에 기본값을 인자로 넘겨 주는 방식으로 할 수 있습니다.

histogram = Hash.new(0)
histogram['key1'] » 0
histogram['key1'] = histogram['key1'] + 1
histogram['key1'] » 1

배열과 해쉬 객체는 매우 유용한 메소드들을 많이 가지고 있습니다. 33page와 278page~317page에 걸쳐 이 메소드들에 대해 자세하게 나와있습니다.

Agile Java 발표 후기

초반에 상당히 긴장을 했다.

갑자기 반장님께서 무서운 포쓰를 발산하시는 바람에 덜덜덜 떨수밖에 없었다.

자칫 내가 잘못된 정보를 전달하면 어떻게 될까..

말을 얼버무리면 안되는데 내가 1장을 정말 다 아는 걸까..

물론 영회형이 같은 발표 팀이라 한편으론 든든 했지만 한편으론 내 자신이 너무 작아보여 떨리고 무서웠다.

회사원들..그리고 아버지 나잇대의 어른.. 난 정말 긴장해 있었다.

그리고 ppt…만드는데 30분정도 걸렸다. 어떠한 내용을 넣어야 할지 감이 잡히질 않았다.

이미 실무에서 개발자로 일하고 계신 분들에게 자바의 매우 기초적인 지식들에 대해 발표하는 건 어색하다고 생각했다.

뭐 앞에서 주름잡기라고 하지 않았던가… 막막했다..

다행히 영회형이 정리 해 둔 내용을 중심으로 요약을 하고 내가 요약한 부분 중에 추가할 것을 붙였다.

영회형은 기본을 넘어 좀더 실용적인 지식들을 전달해줄 준비를 마치고 있었다.

반면에 난 매우 기본 적인 지식에도 긴장한 탓인지 내 기본 지식의 두께인지.. 자신감을 잃고 말았다.

영회형이 주신 조언에 따라 다음 부턴 똑바로하자 기선아…

청중에 초점을 맞추기.

스크린샷/사진/데모가 좋다.

텍스트는 간결하게.

목차를 읽어주기 보단 눈으로 읽을 시간을 주며 골자가 무엇인지를 이야기 함.

Some Basic Ruby

Some Basic Ruby

많은 사람들이 새로운 언어를 배울 때 지겨운 문법을 읽는 것을 좋아하지 않습니다. 그래서 방법을 생각해냈습니다. Ruby 프로그램을 작성할 때 꼭 알아야 하는 것들만 적어 놨습니다. 199페이지 부터 시작하는 18챕터에서 더 자세히 살펴보겠습니다.

간단한 Ruby 프로그램을 보며 시작합시다. 문자열을 반환하는 메소드를 작성했습니다. 이름을 문자열에 추가하여 반환하는 군요. 이 메소드를 몇번 호출해 봤습니다.

def sayGoodnight(name)  
result = "Goodnight, " + name  
return result
end
# Time for bed...
puts sayGoodnight("John-Boy")
puts sayGoodnight("Mary-Ellen")

Ruby는 statement마다 세미콜론(;)을 필요로 하지 않습니다. 그리고 주석은  # 문자로 시작합니다.

메소드는 def 키워드를 사용하여 정의하며 def뒤에 메소드의 이름을 적어주고 메소드의 파라미터들을 괄호() 안에 적어줍니다. 또 루비는 여러 문장을 묶거나 메소드의 정의를 구분하기 위해 중괄호를 사용하지 않습니다. 대신 end로 끝남을 알려줍니다. 메소드의 첫줄은 “Goodnight, “에 파라미터 name을 붙이고 그 다음 줄에서 result를 반환하고 있습니다. result 변수를 선언할 필요가 없습니다. 그냥 대입 할 때 생기게 됩니다.

메소드를 정의한 뒤 우번 호출을 했습니다. 그리고 두 경우다 결과를 puts 메소드에 넘겨줍니다. puts는 아규먼트로 넘어온 값을 단순히 newline과 함께 출력합니다.

Goodnight, John-BoyGoodnight, Mary-Ellen

puts sayGoodnight("John-Boy")” 이 문장은 두개의 메소드 호출을 하고 있습니다. 하나는 sayGoodnight이고 다은으로 puts입니다. 왜 sayGoodnight는 괄호를 사용해서 인자를 넘겨주는데 puts는 안그러냐구요? 그냥 취향입니다. 사실 아래 있는 것들 모두 같은 뜻입니다.

puts sayGoodnight "John-Boy"
puts sayGoodnight("John-Boy")
puts(sayGoodnight "John-Boy")
puts(sayGoodnight("John-Boy"))

하지만 삶은 그렇게 간단하지 않습니다. 괄호를 사용하면 어떤 인자가 어떤 메소드에 사용되는 것인지 헷갈리게 됩니다. 따라서 괄호는 단순한 경우에는 사용하지 않는것을 추천합니다.

이 예제는 Ruby의 문자열(string) 객체도 보여주고 있습니다. string 객체를 생성하는 방법은 다양하지만 가장 흔한 방법으로는 문자열 상수를 사용하는 방법입니다. 문자열 상수는 ” ” 나 ‘ ‘ 로 둘러쌓인 문자열을 말합니다. 이 두 형태의 차이점은 상수를 생성할 때 Ruby가 문자열에 처리를 하는 양이 다르다는 것입니다. ‘ ‘ 로 둘러쌓인 경우에 Ruby는 몇몇 예외처리 들과 매우 소수의 작업을 하며 ‘ ‘ 안에 있는 문자열 상수들이 문자열 값이 됩니다.

” “로 둘러쌓인 경우에 Ruby는 좀더 많은 일을 하는데, 우선. / 로 시작하는 문자를 binary 값을 바꿔줍니다. “\n”과 같은 경우 이 문자열을 newline 문자로 바꿔줍니다.

puts "And Goodnight,\nGrandma"

이 문장의 결과는 아래와 같습니다.

And Goodnight,Grandma

” “를 사용한 문자열은 #{ expression } 을 expression의 값으로 대체 할 수 있습니다. 이것을 사용하여 메소드를
재정의 할 수 있습니다.

def sayGoodnight(name)  result = "Goodnight, #{name}"  return resultend

#{…} 안에 있는 expression이 단순히 global, instance 또는 class 변수 일 경우에는 괄호를 사용하지 않아도 됩니다. string이나 Ruby의 기본 타입들은 47쪽부터 시작하는 챕터5를 보면 됩니다.

마지막으로 이 메소드를 좀더 단순하게 고칠수가 있는데 Ruby의 메소드는 맨 마지막 문장의 값을 return합니다. 따라서 return result문장을 지워도 됩니다.

def sayGoodnight(name)  "Goodnight, #{name}"end

하나만 더 알고 지나갑시다. Ruby의 naming 규칙입니다. 여기서 아직 배우지 않은 class 변수와 같은 말이 나오겠지만 그냥 들어두기 바랍니다.

Ruby는 여러가지 이름을 정할 때 약속이 있습니다. 지역 변수, 메소드 이름, 메소드의 파라미터들은 이름의 첫글자가 소문자로 시작합니다. 전역 변수는 $로 시작하고 인스턴스 변수는 @로 시작합니다. 클래스 변수는 @@이걸로 시작을 합니다. 마지막으로 클래스 이름, 모듈 이름, 상수는 대문자로 시작합니다.

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하면 됩니다.) 마술을 부릴 수도 있습니다.