반복자( iterator )
Posted 2012. 10. 23. 19:01주요 키워드는 "반복적인 행동의 일반화"
- Filed under : 디자인 패턴/4. 반복자( Iterator )
주요 키워드는 "반복적인 행동의 일반화"
명령어 클래스와 인수 클래스를 만들어 명령어 클래스는 명령어에 대해서만 해석하고
인수 클래스는 어떠한 종류의 인수인지만 해석하여 저장하면 그 두 클래스 모두 표현 클래스를
부모에 두고 복합체 패턴 형식으로 결합하여 명령을 수행만 하면 끝이다.
만약 수식이 필요해졌다고 하면 수식 클래스에 표현 클래스를 상속하여 작성하면 된다.
요청 자체를 객체화 하는것. 그리고 서로 다른 요청을 객체화 하여 클라이언트에게
파라미터로 넘겨 줄 수 있게 한다.
명령의 발동과 명령 수행의 분리 패턴하기 때문에 종속성을 제거하여 결합도를 낮출수 있다.
얼마든지 동적으로도 같은 메뉴에 다른 기능을 연결하게 할 수 있다.
명령패턴은 복합체 패턴과 융화되어 매크로 기능을 구현할수있다.
아래 코드는 명령 클래스와 그 서브 클래스를 나타낸 것인데 Execute()라는 매서드를 발동시킴으로써
명령 수행에 대한 일반화를 시켰다. 다른 기능들, 복사하기, 취소하기 등등도 Execute()로 발동된다.
그리고 취소하기 같은 경우, 명령 클래스들에 명령 수행전 명령 히스토리에 해당 명령을 저장하게
함으로써 가능하게 된다. 그 명령 히스토리의 역으로 반대의 명령을 실행하면 취소하기(Undo)가된다.
// 명령 추상 클래스
class Command
{
public:
virtual ~Command();
virtual void Execute() = 0;
protected:
Command();
};
// 문서 열기 클래스
class OpenCommand : public Command
{
public:
OpenCommand( Application*);
virtual void Execute();
protected:
virtual const char* AskFileName();
private:
Application* m_app;
};
// 문서 열기 생성자( 응용프로그램 포인터를 저장 )
OpenCommand::OpenCommand( Application* a )
{
_app = a;
}
// 문서 열기 명령 수행( Document = 응용 프로그램내 문서 클래스 )
void OpenCommand::Execute()
{
const char* name = AskFileName();
if( name != NULL )
{
Document* doc = new Document( name );
_app->Add(doc);
doc->Open();
}
}
메시지를 보내는 객체와 이를 받아 처리하는 객체들 간의 결합도를 없애기 위한 패턴.
하나의 요청에 대한 처리가 반드시 한 객체에서만 이루어지지 않고, 여러 객체에게 그 처리의 기회를
주려는 것이다. 즉, 이 패턴에서는 하나의 서비스 처리를 여러 객체에 나눌 수 있도록 한다.
메시지를 수신하여 처리를 담당할 객체들을 하나의 연결 고리로 만들고, 실제로 요청을 처리하는
객체를 만날 때까지 계속해서 요청을 전달하게 한다.
#include <windows.h>
#include <tchar.h>
#include <memory>
#include <iostream>
#include <deque>
#include <list>
#include <vector>
#include <algorithm>
#include <iomanip>
class Handler
{
public:
typedef int Topic;
public:
explicit Handler (Handler *s=0, Topic t=0) :
Successor_(s),
Topic_ (t)
{
}
virtual ~Handler ()
{
}
public:
virtual void HandleRequest(void)
{
if (Successor_)
{
Successor_->HandleRequest ();
}
else
{
std::cout << "Handler::HandleRequest() : no Successor_" << std::endl;
}
}
virtual void SetHandler (Handler *s=0, Topic t=0)
{
Successor_=s;
Topic_ =t;
}
protected:
Handler* Successor_;
Topic Topic_;
};
class ConcreteHandler1 : public Handler
{
public:
ConcreteHandler1 (Handler *s=0, Topic t=0) :
Handler(s,t)
{
}
public:
virtual void HandleRequest(void)
{
if (Topic_==1)
{
std::cout << "ConcreteHandler1::HandleRequest()" << std::endl;
}
else
{
Handler::HandleRequest();
}
};
};
class ConcreteHandler2 : public Handler
{
public:
ConcreteHandler2 (Handler *s=0, Topic t=0) :
Handler(s,t)
{
}
public:
virtual void HandleRequest(void)
{
if (Topic_==2)
{
std::cout << "ConcreteHandler2::HandleRequest()" << std::endl;
}
else
{
Handler::HandleRequest();
}
};
};
class ConcreteChildHandler : public ConcreteHandler2
{
public:
ConcreteChildHandler (Handler *s=0, Topic t=0) :
ConcreteHandler2(s,t)
{
}
public:
virtual void HandleRequest(void)
{
if (Topic_==3)
{
std::cout << "ConcreteChildHandler::HandleRequest()" << std::endl;
}
else
{
ConcreteHandler2::HandleRequest();
}
};
};
int _tmain(int argc, _TCHAR* argv[])
{
Handler* handler1 = new Handler ();
Handler* handler2 = new ConcreteHandler1 (handler1, 1);
Handler* handler3 = new ConcreteHandler2 (handler2, 1);
Handler* handler4 = new ConcreteChildHandler (handler3, 2);
Handler* handler5 = new ConcreteChildHandler (handler3, 3);
handler1->HandleRequest();
handler2->HandleRequest();
handler3->HandleRequest();
handler4->HandleRequest();
handler5->HandleRequest();
delete handler1;
delete handler2;
delete handler3;
delete handler4;
delete handler5;
return 0;
}
적응자( Adapter) 대 가교(Bridge) 패턴
이둘은 닮았다 .어떤 클래스의 인터페이스 역할을 해주는 면에서 닮았다.
하지만 두 패턴은 그 목적에 차이가 있다. 적응자는 한 클래스의 인터페이스를 현재 적응시키려는
인터페이스에 연결하는 것에 목적이 있기 때문에 인터페이스의 변형이나 발전에는 신경쓰지 않는다.
이에 반해 가교 패턴은 추상적 개념과 구현부를 따로 만들고 이들을 서로 연결시켜주며 시스템의
진화에 따라 새로운 구현의 추가를 고려한다.
적응자는 설계가 완료된 후, 가교는 설계가 완료되기 이전에 적용 된다.
복합체( composite ), 장식자( Decorator ), 프록시( Proxy )
이들 세가지 패턴은 자신의 부모타임의 객체를 클래스내에 소유한다는 점에서 비슷 하다.
하지만 역시나 목적이 다르다 .복합체 패턴은 실제 객체와 그 객체들을 담는 컨테이너 객체를 같은
타입으로 다룰 수 있게 하여 인터페이스를 일관되게 하는게 목적이다. 이에 반해 장식자 패턴은
인터페이스의 일관성도 있겠지만 그보다도 객체에 대한 추가 기능을 동적으로 추가하여 객체를
합성할 수 있게 하는 것이 그 목적이다.
프록시 패턴은 어떤 추가적인 행동을 첨가하는데서 장식자 패턴과 비슷하지만 프록시는 클래스에
기능을 추가하여 더 무겁게 하는 것이 아닌 제어에 있어서 더욱 용이하게 하는 패턴이다.
플라이급 패턴은 객체에 대한 공유를 가능하게 하는 패턴으로 같은 객체의 중복된 사용을 그 수만큼
생성하는 것이 아니라 플라이급 공장 객체를 통해 필요한 객체를 제공받는 형태이다.
본래 객체의 대리 객체 패턴.
즉, 참조 카운팅 포인터 객체이다.
복잡한 객체를 여러개 복사해야 하는 상황이 있다면 어떻게 보면 flyweight와 비슷 하다.
둘다 원본 복합 객체에 대한 참조를 포함한다.
그림이 8장이 있다 치면. 현재 1개의 그림만 보이면 되고
이후 나올 가능성이 있는 7개는 꼭 미리 로드 할 필요가 없다.
물론 메모리가 넉넉하고 로드 비용이 없다고 가정하면 미리 로드 하는것이 좋겠지만
메모리는 한정되어 있고 로드와 해제에 드는 비용도 있으므로 보이는 부부만 그리는게
더 좋은 방법이다.
하지만 문제는 이미지 객체를 할당해놓고 이미지를 실제 할당하지 않아야 하는데. 이미지 객체를
직접 할당하면 할당시 로드를 하게 되는 것이므로 다른 방법이 필요하다. 이때 필요한 것이
대리 객체인 프록시 객체이다.
위 그림을 보면 이미지 프록시 객체가 있는데 그 객체를 이용한다는 뜻이다.
위 그림에는 나와있지 않지만 프록시 객체를 생성시 실제 용량을 차지하는 이미지를 로드하는 코드가
생성자에 있지 않아서 결과적으로 로드가 이루어지지 않은 가상의 이미지 객체가 된다.
이런 식으로 문서의 이미지를 객체화 시켜놓고 실제 모니터 출력 부분에 속할때 Draw명령이 떨어지고
Draw명령 속에서 이미지를 생성하고 그 이미지의 Draw명령을 수행한다.
이것은 전문 용어로 Copy-On-Write라는 기법인데 간단히 말해서 "쓸때쓰자" 이다.
사실 사본의 복사 시기에 관련된 용어이지만 이 상황에도 어울리는 용어다.
Draw가 될때만 이미지를 생성해서 그 이미지를 그리는 실제 이미지가 없어도 문서는 가상 이미지 객체
(프록시 객체)를 생성했으므로 이미지가 있는것으로 취급하며 이미지가 실제로 필요할때 생성하는 유
용한 기법이다.
그림대로 코드를 작성하면
// 문서 생성
TextDocument* text = new TextDocument;
text->Insert( new 이미지Proxy("그림1"));
이렇게 문서에 프록시 형태로 생성하여 삽입해놓으면 초기 할당 비용도 아끼고 메모리도 아낀것이 됨
프록시 객체는 이외에도 여러 다른 역할을 수행 할 수 있습니다.
1. 원격지 프록시 : 현재 공간과는 다른 공간에 존재하는 객체를 대리하는 프록시로써, 현재 공간에 존재하게 된다. 즉, 현재 공간에서 프록시 객체에게 명령을 내리면 프록시 객체는 원격지의 실제 객체에게 명령을 전달해주는 역할을 하게 된다.
2. 가상 프록시 : 방금 위에서 살펴 본 문서 안의 이미지 프록시 객체가 가상 프록시 이다. 간단히 말해 요청이 있을때에만 고비용 객체를 생성 한다.
3. 보호용 프록시 : 방금 위에서 살펴 본 문서 안의 이미지 프록시 객체가 가상 프록시 이다. 간단히 말해 요청이 있을때에만 고비용 객체를 생성한다.
4. 스마트참조자 : 프록시는 어떤 면에서는 포인터의 역할과도 비슷하다. 어떤 객체에 대한 참조자를 단순히 포인터로 지정하지 않고 부가적인 기능이 있는 똑똑한 참조자의 역할로 수행하게 할 수 있다.참조 횟수를 저장하여 객체의 할당, 해제를 자동으로 한다든지, 처음 참조 되는 시점에 메모리에 할당하게 한다든지, 실제 객체에 접근하기 전에 병행 제어를 위해 객체에 대해 잠금을 걸 수 있다.
class RealImage
{
private:
m_id;
public:
RealImage(int i)
{
m_id = i;
cout << " $ $ ctor : " << m_id << '\n';
}
~RealImage()
{
cout << " dtor : " << m_id << '\n';
}
void draw()
{
cout << " drawing image " << m_id << '\n";
}
};
class Image
{
private:
RealImage *m_the_real_thing;
int m_id;
static int s_next;
public:
Image()
{
m_id = s_next++;
m_the_real_thing = 0;
}
~Image()
{
delete m_the_real_thing;
}
void draw()
{
if( !m_the_real_thing )
m_the_real_thing = new RealImage( m_id );
m_the_real_thing->draw();
}
};
int Image::s_next = 1;
void main()
{
Image images[5];
for( int i ; true ; )
{
cout << "Exit[0], Image[1-5] : ";
cint >> i;
if( i == 0 )
break;
images[i - 1].draw();
}
}
->Exit[0], Image[1-5] : 2
-> $$ ctor : 2
-> drawing image 2
->Exit[0], Image[1-5] : 4
-> $$ ctor : 4
-> drawing image 4
-> Exit[0], Image[1-5] : 2
-> drawing image 2
-> Exit[0], Image[1-5] : 0
-> dtor : 4
-> dtor : 2
중복 되는 객체의 본질 공유 패턴
Mon Mon Mon Mon Mon
Missile Missile Missile
맵에 몬스터가 나오고 미사일을 쏜다.
몬스터 3마리는 모두 외관이 동일하고, 동작에 대한 표현이 같은데
그것을 포함한 객체를 몬스터 숫자만큼 생성하는건 너부 비효율 적이다.
이 본질적 요소를 기준으로 공유될 수 있는 객체를 분류하는 것이 플라이급 패턴.
클래스의 경량화를 목적으로 다수의 객체로 생성 될 경우 모두가 갖는 본질적인 요소를 클래스화 하여
공유함으로써 메모리 절감의 효과를 보는 패턴. 부가적인 요소는 사용자가 관리하며 그 요소를 플라이
급 객체에서 매개변수로 받아 본질 요소를 이룔 할 수 있다.
이건 뭐.. 옜날에 매니저 클래스 들이네 ㅡ,.ㅡ
알게 모르게 고민하고 짯던 부분들이 다 패턴들이었군 ㅇ,.ㅇ
복잡한 서브 시스템을 간단하게 사용하기 위한 패턴.
인터페이스를 앞에 제공해 주는 패턴.
파란선 참고 ↗
상용 단계가 아니라 개발자가 조작하듯 직접 구성요소들을 조작 해야 한다.
사용자는 냄비를 할당하고 물을 끓인 후 튀긴 면발을 할당하여 면을 넣고 부재료도 할당하여
직접 넣어야 한다. 이 모든 과정을 사용자가 직접해야 한다. 개발자는 수월하겠지만
일반 사용자는 이런 복잡한 상태라면 먹지 않을 것이다.
위 그림처럼 됬을 경우. 뭔가 되게 불편하고, 냄비의 구조나 면발의 사용법이 달라졌을때 이전 사용법
으로 조작하면 꼼짝없이 사용시 오류가 나버린다.이런 불편을 해소하고 객체에 사용자에 대한 강한
결합도를 낮추기 위해 인터페이스를 두는 것이 퍼사드 패턴 이다.
사용자는 라면 자판기에 원하는 면발과 부재료만 누르면 알아서 라면이 나온다.
복잡한 구조를 몰라도 사용하는데 무리가 없다.
위 패턴은 Builder패턴과 상당히 유사하다.
하지만 빌더는 생성의 구성 순서를 정하여 생성 전용으로 쓰는 패턴이고
퍼사드는 생성 뿐만 아니라 내부의 다른 기능들까지 인터페이스 역할을 해낼 수 있다.
예를 들면 라면 생성뿐만 아니라 라면 잔여물 처리나 라면 추가 같은 기능도 넣을 수 있다.
퍼사드에선 각 객체들로 단방향으로 설계가 되어있기 때문에 자연스럽게 결합도가 낮아지고,
서브 클래스 추가나 변경에 더 유연하게 대철 할 수 있는 것이다.
이런 식은 우리도 많이 쓰곤 한다. 필요한 각종 기능들을 한 클래스의 함수에 몰아서 한번에 쫙 호출
하는 그런 패턴, 그런 인터페이스의 단순화가 많아질수록 시스템에 대한 접근성이 높아지고 단위별로
테스트가 가능하기 때문에 오류에 대한 처리도 상당히 유리하게 된다.
정리하면
퍼사드 패턴은 복잡한 시스템에 대하여 단순한 인터페이스를 제공함으로써 사용자와 시스템 간, 또는
여타 시스템과의 결합도를 낮춰주고, 시스템 구조에 대한 파악이 쉬워지는 패턴이다.
오류에 대해 단위별로 확인 할 수 있고, 사용자에게 단순한 인터페이스를 제공함으로써 접근성을 높임.
#include <iostream>
using namespace std;
// 면발 클래스
class Noodle
{
public:
virtual Noodle* NoodleInsert() = 0; // 면 넣기
virtual void Print() = 0; // 면 정보 출력
};
// 생서 면발 클래스
class LiveNoodle : public Noodle
{
public:
virtual Noodle* NoodleInsert()
{
cout << "생성 면발을 넣습니다" << endl;
return this;
}
virtual void Print()
{
cout << "생성 면발";
}
};
// 튀김 면발 클래스
class FriendNoodle : public Noodle
{
public:
virtual Noodle* NoodleInsert()
{
cout << "튀김 면발으 넣습니다" << endl;
return this;
}
virtual void Print()
{
cout << "뒤김 면발";
}
};
// 부재료 클래스
class SubMaterial
{
public:
virtual SubMaterial* SubMaterialInsert() = 0; // 부재료 넣기
virtual void Print() = 0; // 재료 출력
};
// 치즈 클래스
class Cheese : public SubMaterial
{
public:
virtual SubMaterial* SubMaterialInsert()
{
cout << "치즈를 넣습니다" << endl;
return this;
}
virtual void Print()
{
cout << "치즈 ";
}
};
// 만두 클래스
class Dumpling : public SubMaterial
{
public:
virtual SubMaterial* SubMaterialInsert()
{
cout << "만두를 넣습니다" << endl;
return this;
}
virtual void Print()
{
cout << "만두 ";
}
};
// 냄비 클래스
class Pot
{
private:
Noodle *noodle;
SubMaterial *Material;
public:
Pot()
{
noodle = NULL;
Material = NULL;
}
~Pot()
{
if( noodle )
delete noodle;
if( Material )
delete Material;
}
public:
void Boil() // 물끓이기
{
cout << "냄비에 물을 끓입니다" << endl;
}
void InsertNoodle( Noodle *noodle) // 면발 넣기
{
this->noodle = noodle;
}
void InsertMaterial( SubMaterial *Material ) // 재료 넣기
{
this->Material = Material;
}
void Print() // 라면 정보 출력
{
noodle->Print();
Material->Print();
cout << "라면 입니다" << endl;
}
};
// 라면 자판기 클래스
class RamenMachine
{
public:
void Cooking( Pot *pot, Noodle *noodle, SubMaterial *Material )
{
pot->Boil();
pot->InsertNoodle(noodle->NoodleInsert());
pot->InsertMaterial( Material->SubMaterialInsert() );
cout << "조리가 완료 되었습니다" << endl;
}
};
void main()
{
RamenMachine Machine;
Pot *pot = new Pot;
Machine.Cooking( pot, new LiveNoodle, new Dumpling );
cout << endl <<"--라면 정보--" << endl;
pot->Print();
delete pot;
}
클래스 기능의 동적인 추가를 가능하게 하는 구조 !!
클래스의 기능이 추가된 확장 클래스 객체 생성을 런타임에 동적으로 할 수 있게 하는 패턴
장식적인 의미의 추가 기능들과 오리지널 클래스를 분리하여 합성 객체에 대한 융통성을 제공함.
하지만 장식이 될 객체의 최상위 부모 클래스는 작고 가벼워야 효율적이다. 왜냐면
장식이 중복된 규모있는 클래스를 객체화 할 경우 부모의 크기에 따라 객체의 크기가 커지고
기능적으로 장식자에 부담으로 작용 되기 때문.
중간 클래스로 Decorator를 두는 것이 좋다. 두지 않으면 최상위 클래스가 Deco객체도 관리하게
되므로 Deco객체만의 Interface를 가질 수 없게 된다.
Decorator클래스는 구현될 일이 없으므로 abstract class로 둔다.
사용 예)
게임 플레이 도중 새로운 아이템을 추가 시키거나 삭제 시킬때.
오락실 비행기 게임, 아이템 먹을수록 미사일 파워 증가.
처럼 다른객체에 영향을 주지 않고 객체에 기능을 추가하고 싶을때
상속을 통한 기능확장이 어려울때.
단점.
( 장식자 개수 * 부모 클래스 크기 ) 만큼 객체 하나의 부담이 증가한다.
장식자나 오리지널 객체 자체가 가벼워져서 그 의미가 뚜렷하지 않으면 그 수가 많아질경우
관리가 힘들어진다.
전략(Strategy) 패턴과의 비교.
객체의 내부가 아닌 외부적인 기능의 추가에 적합것이 장식자 패턴
배주 자체에 기능의 변경이라던지 추가에는 전략 패턴이 적합.
전략 패턴은 직접 오리지널과 같은 부모를 상속받는 것이고 따로 기능만 정의해서 오리지널을 참조를 통해 접근 하는 패턴.
#include <iostream>
using namespace std;
// 최상위 순수 가상. 작고 가벼워야 효율적
//인간클래스
class Human
{
public:
virtual ~Human(){};
public:
virtual void Draw() = 0;
};
// 여자 클래스
class Girl : public Human
{
public:
virtual void Draw()
{
cout << "여자 출력" << endl;
}
};
// 장식 클래스
class Decorator : public Human
{
protected:
Human *ori;
public:
Decorator( Human *ori )
{
this->ori = ori;
}
virtual ~Decorator()
{
delete ori;
}
public:
virtual void Draw()
{
ori->Draw();
}
};
// 귀걸이 ㅡㄹ래스
class Earring : public Decorator
{
public:
Earring( Human *ori ) : Decorator( ori )
{
}
virtual void Draw()
{
Decorator::Draw();
cout << "귀걸이 출력" << endl;
}
};
// 안경 클래스
class Glass : public Decorator
{
public:
Glass( Human *ori ) : Decorator( ori )
{
}
virtual void Draw()
{
Decorator::Draw();
cout << "안경 출력" << endl;
}
};
void main()
{
cout << "---안경 여자 ---" << endl;
Human *girl = new Girl;
Human *GlassGirl = new Glass( girl ); // 안경 여자
GlassGirl->Draw();
cout << endl;
cout << "---안경 귀걸이 여자---" << endl;
Human *girl2 = new Girl;
Human *GlassEarringGirl = new Glass( new Earring( girl2) ); // 안경 귀걸이 여자
GlassEarringGirl->Draw();
delete GlassGirl;
delete GlassEarringGirl;
}
-> --- 안경 여자 ---
-> 여자 출력
-> 안경 출력
-> --- 안경 귀걸이 여자 ---
-> 여자 출력
-> 귀걸이 출력
-> 안경 출력
어느 객체가 다른 객체의 일부가 되는 관계는 Composite(구성) 관계라 한다.
여러개의 객체를 하나의 객체로 보이게 하고 싶을때 사용
기본 제품으로 새로운 제품을 만들어 사용 하고 싶을 경우
기본 객체 : 원래 존재하는 객체
구성 객체 : 기본 객체들로 이루어진 새로운 객체( 삼각형 + 사각형 = 집 )
구성 객체를 생성하고 그 안에 기본 객체들을 포함시켜 관리한다
하지만 이럴 경우 자료형이 서로 완전히 달라 따로 관리해야 하므로 불편하다.
구성 객체를 생성함에 있어 부모 클래스의 어떠한 인터페이스를 포함시키고 각 인터페이스의 구현은
어떻게 할 것인가가 중요하다.
Composite패턴의 중요함은 Composite 객체도 사용자가 느끼기에 같은 객체로 느껴야 하기 때문에,
Composite 객체에만 필요한 인터페이스라도 다른 기본 객체에도 제공 되어져야 한다.
따라서 최상위 클래스에서는 virtual 함수를 정의하고 구현하지 않는 식으로 기본 클래스에
인터페이스만 제공 할 수 있다.
장점.
기본 객체와 구성 객체를 구별하지 않고 소스코드를 작성 할 수 있어 좋다.
새로운 클래스의 추가가 용이하다.( 기존 소스코드의 변경이 필요 없다.)
복합체는 복합체를 포함 할 수 있어 트리구조로 컨테이너 객체를 표현 할 수 있다.
#include <iostream>
#include <vector>
using namespace std;
class Component
{
public:
virtual void traverse() = 0;
};
class Leaf : public component
{
private:
int value;
public:
Leaf( int val )
{
value = val;
}
void traverse()
{
cout << value << ' ';
}
};
class Composite : public Component
{
private:
vector < Component * > chileren;
public:
void add( Component *ele )
{
children.push_back(ele);
}
void traverse()
{
for( int i=0 ; i<children.size() ; i++ )
{
children[i]->traverse();
}
}
};
void main()
{
int i, j;
Composite containers[4];
for( i=0 ; i<4 ; i++ )
{
for( j=0 ; j<3 ; j++ )
{
containers[i].add( new Leaf(i * 3 + j) );
}
}
for( i=1 ; i<4 ; i++ )
containers[0].add( &( containers[i] ) );
for( i=0 ; i<4 ; i++ )
{
containers[i].traverse();
cout << endl;
}
}
-> 0 1 2 3 4 5 6 7 8 9 10 11
-> 3 4 5
-> 6 7 8
-> 9 10 11