OpenGL - 로봇 팔 만들기

설명

로봇의 팔을 만들어서 회전하고 꺽을수 있다.


이번에도 마찬가지로 소스에 대한 설명은 주석을 참조하세요

또한 생략된 소스는 맨 앞의 글을 참조하세요

2017/09/12 - [SW/OpenGL] - OpenGL - 사각형 회전, 색바꾸기



로봇의 base

void base(void)
{
	glPushMatrix();//push
	{
		glColor3f(100);//red
		glTranslatef(0, -0.80);//y axis
		glRotatef(-90100);//x axis
		glRotatef(Base_Angle001);//z axis
		gluCylinder(p0.50.50.3201);//draw cylinder
	}
	glPopMatrix();//pop
}


로봇의 lower

/// <summary>
/// arm's lower
/// </summary>
void lower_arm(void)
{
	glPushMatrix();//push
	{
		glColor3f(010);//green
		glRotatef(Base_Angle010);//rotate with y axis
		glTranslatef(0, -0.70);//translate with axis
		glRotatef(Lower_Arm_Angle001);//rotate with z axis
		glTranslatef(00.50);//translate with y axis
		glScalef(0.210.2);//scaling
		glutWireCube(1);//draw cube
	}
	glPopMatrix();//pop
}


로봇의 upper

/// <summary>
/// arm's upper
/// </summary>
void upper_arm(void)
{
	glPushMatrix(); //push
	{
		glColor3f(001); // blue
		glRotatef(Base_Angle010); //rotate y axis
		glTranslatef(0, -0.70);//translate y axis
		glRotatef(Lower_Arm_Angle001);//rotate z axis
		glTranslatef(01.00);//translate y axis
		glRotatef(Upper_Arm_Angle001);//rotate z axis
		glTranslatef(00.40);//translate y axis
		glScalef(0.20.80.2);//scaling
		glutWireCube(1);//draw cube
	}
	glPopMatrix();//pop
}


렌더링 함수

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.50.50.5000010);//setting camera viewpoint
	base(); //draw base
	lower_arm(); //draw lower part
	upper_arm(); //draw upper part
	glutSwapBuffers();
}


스페셜 키 이벤트 (화살표4개, home, end)

/// <summary>
/// Special key event
/// </summary>
/// <param name="key">Special key.</param>
/// <param name="x">The x</param>
/// <param name="y">The y</param>
void specialkeys(int keyint xint y)
{
	switch (key)
	{
	case GLUT_KEY_UP//up arrow
		Lower_Arm_Angle += 2;
		break;
	case GLUT_KEY_DOWN//down arrow
		Lower_Arm_Angle -= 2;
		break;
	case GLUT_KEY_LEFT//left arrow
		Base_Angle -= 2;
		break;
	case GLUT_KEY_RIGHT//right arrow
		Base_Angle += 2;
		break;
	case GLUT_KEY_HOME//home key
		Upper_Arm_Angle += 2;
		break;
	case GLUT_KEY_END//end key
		Upper_Arm_Angle -= 2;
		break;
	default:
		break;
	}
}


메인함수

void main(int argccharargv[])
{
	glutInit(&argcargv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(500500);
	glutCreateWindow("Simple");
	glutDisplayFunc(RenderScene);
	glutReshapeFunc(ChangeSize);
	glutSpecialFunc(specialkeys); //Special key event
	glutMouseFunc(mouseButton);
	glutMotionFunc(mouseMotion);
	glutTimerFunc(1000 / 30timer1);
	glutKeyboardFunc(keyboard);
	init();
	SetupRc();
	glutMainLoop();
}


타이머 함수

void timer(int value)
{
	if (rot)
	{
		theta += 2.0;
		if (theta >= 360.0)
			theta -= 360.0;
	}
	else
	{
		theta -= 2.0;
		if (theta <= 360.0)
			theta += 360.0;
	}
	glutTimerFunc(1000 / 30timer1);
	glutPostRedisplay();
}


결과


OpenGL - 정육면체 xyz축 회전


설명


정육면체를 'X'를 눌렀을때는 X축 회전, 'Y'를 눌렀을땐 Y축회전, 'Z'를 눌렀을때는 Z축 회전시킨다.



소스설명


앞의 소스와 중복되는 부분은 변경이 있기전까진 생략한다.

궁금하신분은 맨 앞의 게시물을 확인바랍니다.

2017/09/12 - [SW/OpenGL] - OpenGL - 사각형 회전, 색바꾸기



선언

static bool mouseLeftDown;//checking mouse click
static float point[2][2];
void mouseButton(int buttonint stateint xint y);
void mouseMotion(int xint y);
void keyboard(unsigned char keyint xint y);
void timer(int value);
void quad(int aint bint cint d);
static bool R = true;
static bool G = true;
static bool B = true;
static float theta;
bool rot = true;
int rx_axis = 0y_axis = 1z_axis = 0;


점8개와 색8개

/// <summary>
/// eight point to draw a cube
/// </summary>
GLfloat vertices[8][3] = { { -1, -11 },{ -111 },
{ 111 },{ 1, -11 },{ -1, -1, -1 },
{ -11, -1 },{ 11, -1 },{ 1, -1, -1 } };
 
 
 
/// <summary>
/// eight color each points
/// </summary>
GLfloat colors[8][3] =
{ { 001 },{ 011 },
{ 111 },{ 101 },
{ 000 },{ 010 },
{ 110 },{ 100 } };

colors는 r,g,b 값으로 색을 조합한다.



x,y,z 키에 대한 이벤트(x축, y축, z축)

void keyboard(unsigned char keyint xint y)
{
	switch (key)
	{
	case 'q' | 'Q':
		exit(0); break;
	case VK_ESCAPE:
		exit(0); break;
	case 1:
		exit(0); break;
	case 2:
		exit(0); break;
	case 'x' | 'X':
	{
		x_axis = 1;
		y_axis = 0;
		z_axis = 0;
		break;
	}
	case 'y' | 'Y':
	{
		x_axis = 0;
		y_axis = 1;
		z_axis = 0;
		break;
	}
	case 'z' | 'Z':
	{
		x_axis = 0;
		y_axis = 0;
		z_axis = 1;
		break;
	}
	default:
		break;
	}
}


렌더링함수

void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.50.50.5000010); //view point
	glRotatef(thetax_axisy_axisz_axis);//rotate cube
 
	//draw a cube with 6 quadrature
	quad(0321);
	quad(2376);
	quad(3047);
	quad(1265);
	quad(4567);
	quad(5401);
	glEnd();
	glutSwapBuffers();
}


사각형을 그린다 위의 함수에서 6개의 사각형을 그리는데 사용하는 함수 quad

void quad(int aint bint cint d)
{
	glBegin(GL_QUADS);
	glColor3fv(colors[a]); glVertex3fv(vertices[a]);
	glColor3fv(colors[b]); glVertex3fv(vertices[b]);
	glColor3fv(colors[c]); glVertex3fv(vertices[c]);
	glColor3fv(colors[d]); glVertex3fv(vertices[d]);
}


마우스 클릭, 모션 함수 (마우스 왼쪽 클릭하면 회전방향이 바뀐다.)

void mouseButton(int buttonint stateint xint y)
{
	//mouse left button click
	if (button == GLUT_LEFT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
			if (!mouseLeftDown)
			{
				if (rot)
					rot = false;
				else
					rot = true;
			}
		}
		else if (state == GLUT_UP)
		{
			if (mouseLeftDown)
				mouseLeftDown = false;
		}
	}
	//mouse right button click
	else if (button == GLUT_RIGHT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
 
		}
		else if (state = GLUT_UP)
		{
 
		}
	}
	glutPostRedisplay();
}
 
void mouseMotion(int xint y)
{
	if (mouseLeftDown)
	{
		double viewport[4];
		glGetDoublev(GL_VIEWPORTviewport);
 
		point[1][0] = x / (float)viewport[2] * 500;
		point[1][1] = (viewport[3] - y) / (float)viewport[3] * 500;
	}
	glutPostRedisplay();
}


정육면체를 회전하기 위한 타이머

void timer(int value)
{
	if (rot)
	{
		theta += 2.0;
		if (theta >= 360.0)
			theta -= 360.0;
	}
	else
	{
		theta -= 2.0;
		if (theta <= 360.0)
			theta += 360.0;
	}
	glutTimerFunc(1000 / 30timer1);
	glutPostRedisplay();
}




초기화

void init(void)
{
	theta = 0.0f;
	glutTimerFunc(10timer1);
}
 
void SetupRc(void)
{
	glClearColor(1.0f1.0f1.0f1.0f);
	glEnable(GL_DEPTH_TEST);
}



결과



OpenGL - 3점을 찍어 삼각형 그리기


설명


마우스로 폼위의 3점을 찍으면 그 점을 꼭지점으로 삼각형을 그린다.




소스설명


설명은 주석으로 대체하였다.


parameter

#define POINTS 100
 
GLfloat pos[POINTS][2];//point array
GLfloat Width = 600.0//form width
GLfloat Height = 600.0//form height
GLint n = 0//number of points


렌더링함수

/// <summary>
/// Renders the scene.
/// </summary>
void RenderScene(void)
{
	int i;
	glClear(GL_COLOR_BUFFER_BIT);
	glColor3f(100);//red color
	glPointSize(10.0f);//size 10
	glEnableClientState(GL_VERTEX_ARRAY);// enable array
 
	
	for (i = 0i < 3i++)
	{
		glBegin(GL_POINTS);//dot
		glVertex2f(pos[i][0], pos[i][1]);//dot position
	}
	if (i>2 && (pos[2][0] != 0))//draw triangle when the number of points are greater than two
	{
		glBegin(GL_TRIANGLES);
		glVertex2f(pos[0][0], pos[0][1]);
		glVertex2f(pos[1][0], pos[1][1]);
		glVertex2f(pos[2][0], pos[2][1]);
	}
	glEnd();
	glFlush();//flush
}


마우스 클릭 이벤트

/// <summary>
/// Mouse button event.
/// </summary>
/// <param name="button">The button.</param>
/// <param name="state">The state.</param>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
void mousebutton(int buttonint stateint xint y)
{
	if (button == GLUT_LEFT_BUTTON)
	{
		if (state == GLUT_DOWN)
		{
			pos[n][0] = x / Width;
			pos[n][1] = (Height - y) / Height;
			if (n > 2)//when then number of points greater than two
			{
				pos[n % 3][0] = pos[n][0];//x
				pos[n % 3][1] = pos[n][1];//y
			}
			n++;
			RenderScene();//rendering
		}
	}
}


윈폼 크기 변경 함수

/// <summary>
/// winform size changed
/// </summary>
/// <param name="w">width</param>
/// <param name="h">height</param>
void ChangeSize(int wint h)
{
	GLfloat asepectRatio;
 
	Width = w;
	Height = h;
 
	if (Height == 0)
		Height = 1;
	glViewport(00wh);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	
	gluOrtho2D(0.01.00.01.0);
}
void main(int argcchar *argv[])
{
	glutInit(&argcargv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowSize(WidthHeight);
	glutInitWindowPosition(00);
	glutCreateWindow("ex2");
	glClearColor(1.0f1.0f1.0f1.0f);
	glMatrixMode(GL_PROJECTION);
 
	glutDisplayFunc(RenderScene);
	glutReshapeFunc(ChangeSize);
	glutMouseFunc(mousebutton);
 
	glutMainLoop();
}


결과물


OpenGL - 사각형 회전, 색바꾸기

OpenGL의 기초적인 함수를 사용해본다.


설명

r을 누르면 사각형의 회전방향이 바뀌고, 폼을 클릭하면 사각형의 색이 랜덤으로 바뀐다.


소스설명


키보드 입력등록

void menu(int item)
{
	keyboard((unsigned char)item00);
}


키보드 이벤트 등록

/// <summary>
/// key event registration
/// </summary>
/// <param name="key">The key.</param>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
void keyboard(unsigned char keyint xint y)
{
	switch (key)
	{
	case 'q' | 'Q':
		exit(0); break;
	case VK_ESCAPE:
		exit(0); break;
	case 1:
		exit(0); break;
	case 2:
		exit(0); break;
	case 'R' | 'r'//change rotate direction
		rotate(); break;
	default:
		break;
	}
}


렌더링함수

/// <summary>
/// Renders the scene.
/// </summary>
void RenderScene(void)
{
	glClear(GL_COLOR_BUFFER_BIT);
 
	float cos_th = cos(theta * 3.14159 / 180.0);
	float sin_th = sin(theta * 3.14159 / 180.0);
 
	glColor3f(abc);
 
	//draw polygon
	glBegin(GL_POLYGON);
	{
		glVertex2f(cos_thsin_th);
		glVertex2f(-sin_thcos_th);
		glVertex2f(-cos_th, -sin_th);
		glVertex2f(sin_th, -cos_th);
	}
	glEnd();
	glutSwapBuffers();
}


초기화함수

void init(void)
{
	mouseLeftDown = false;
 
	point[0][0] = 0;
	point[0][1] = 0;
	point[1][0] = 0;
	point[1][1] = 0;
 
	theta = 0;
}
void SetupRc(void)
{
	glClearColor(1.0f1.0f1.0f1.0f);
}


윈폼의 크기가 변할때를 계산

/// <summary>
/// Changes the size.
/// when form size change
/// </summary>
/// <param name="w">width</param>
/// <param name="h">height</param>
void ChangeSize(int wint h)
{
	GLfloat aspectRatio;
 
	if (h == 0)
		h = 1;
 
	glViewport(00wh);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
 
	aspectRatio = (GLfloat)w / (GLfloat)h;
	if (w <= h)
	{
		gluOrtho2D(-2.02.0, -2.0*(float)h / (float)w2.0*(float)h / (float)w);
	}
	else
	{
		gluOrtho2D(-2.0*(float)w / (float)h2.0*(float)w / (float)h, -2.02.0);
	}
 
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}


사각형 회전을 위한 타이머

void timer(int value)
{
	if (rot)//rotate right
	{
		theta += 2.0;
		if (theta >= 360.0)
			theta -= 360.0;
	}
	else//rotate left
	{
		theta -= 2.0;
		if (theta <= 360.0)
			theta += 360.0;
	}
	glutTimerFunc(1000 / 30timer1);
	glutPostRedisplay();
}
 
void rotate()//exchange rotate direction
{
	if (rot)
		rot = false;
	else
		rot = true;
}


마우스 위치 함수

/// <summary>
/// Mouses the motion.
/// </summary>
/// <param name="x">The x.</param>
/// <param name="y">The y.</param>
void mouseMotion(int xint y)
{
	if (mouseLeftDown)
	{
		double viewport[4];
		glGetDoublev(GL_VIEWPORTviewport);
 
		point[1][0] = x / (float)viewport[2] * 500;
		point[1][1] = (viewport[3] - y) / (float)viewport[3] * 500;
	}
	glutPostRedisplay();
}


마우스 클릭 이벤트

/// <summary>
/// Mouse click event
/// </summary>
/// <param name="button">The button.</param>
/// <param name="state">button state.</param>
/// <param name="x">mouse's x</param>
/// <param name="y">mouse's y</param>
void mouseButton(int buttonint stateint xint y)
{
	int r;
	if (button == GLUT_LEFT_BUTTON//mouse left button
	{
		if (state == GLUT_DOWN)//clicked
		{
			if (!mouseLeftDown)
			{
			}
			r = rand() % 3;
			if (r == 0)
			{
				if (a == 0)
					a = 1;
				else
					a = 0;
				glutPostRedisplay();
			}
			else if (r == 1)
			{
				if (b == 0)
					b = 1;
				else
					b = 0;
				glutPostRedisplay();
			}
			else
			{
				if (c == 0)
					c = 1;
				else
					c = 0;
				glutPostRedisplay();
			}
		}
		else if (state == GLUT_UP)//unclicked
		{
			if (mouseLeftDown)
				mouseLeftDown = false;
		}
	}
}


가장 중요한 메인함수

/// <summary>
/// Mains the specified argc.
/// </summary>
/// <param name="argc">The argc.</param>
/// <param name="argv">The argv.</param>
void main(int argccharargv[])
{
	glutInit(&argcargv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
	glutInitWindowSize(600600);//form's size
	glutCreateWindow("ex1");//form's name
	glutDisplayFunc(RenderScene);//display function registration
	glutReshapeFunc(ChangeSize);//reshape function registration
 
	glutMouseFunc(mouseButton);//mouse click function
	glutMotionFunc(mouseMotion);//mouse move function
	glutTimerFunc(1000 / 30timer1); //timer
	glutKeyboardFunc(keyboard);//keyboard functiom
 
	glutCreateMenu(menu);//when right mouse clicked
	glutAddMenuEntry("1"1);
	glutAddMenuEntry("2"2);
	glutAttachMenu(GLUT_RIGHT_BUTTON);
 
	init();
	SetupRc();
	srand(time(NULL));
	glutMainLoop();
}




Template Method Pattern (템플릿메소드패턴)

Template Method Pattern

 


정의


상위 class에서 처리의 흐름을 제어하고, 하위class에서 처리의 내용을 구체화 한다


여러 class에서 공통되는 사항은 상위 추상 class에서 구현하고, 각각의 세부내용은 하위 class에서 구현한다


그 결과 코드의 중복을 줄이고, Refactoring에 유리한 패턴으로 상속을 통한 확장 개발 방법으로 Strategy Pattern과 함께 많이 사용되는 pattern이다.


 

Hook method


필수적이지 않은 부분으로 필요에 따라 선택적으로 sub-class에서 구현할 경우 사용한다


Abstract class에서 정의된 어떠한 행도 하지 않을 수 있고, sub-class의 공통된 동작을 할 수도 있다


또한, sub-class에서 hook method를 재정의 하여 사용하는 방법도 있다


Sub-class에 따라 algorithm에 원하는 내용을 추가할 수 있도록 하는 것이 hook-method의 존재 이유다.


 

요구사항


1.    멤버 함수들의 접근 범위 지정을 명확히 한다.


2.    가상함수, 일반함수로 선언 하는 것 에 대한 기준이 필요


3.    Virtual 함수의 수를 최소화한다.



l  Hollywood Principle

Sub-classupper-class에 구성요소로써 활용될 수 있지만, sub-class upper-class를 호출하여 자신을 호출하게 하면 안된다는 것이다


Template Method Pattern에서도 이러한 원칙을 따르고 있는데


설명을 더하면, abstract class에서는 sub-class에 있는 기능을 호출할 수 있지만


sub-class에서는 abstract-class template method를 호출하거나 수정할 수 없다는 것이다.

 


장점


상위위 class template method에서 알고리즘이 기술되어 있으므로,


하위 class에서는 알고리즘을 일일이 기술할 필요가 없다.


 

소스코드


상위클래스

public abstract class CaffeineBeverageWithHook {

         void prepareRecipe(){

                 boilWater();

                 brew();

                 pourInCup();

                 if(customerWantsCondiments()){

                          addCondiments();

                 }

         }

 

         abstract void brew();

         abstract void addCondiments();

 

         void boilWater(){

                 System.out.println("Boiling water");

         }

 

         void pourInCup(){

                 System.out.println("Pouring into cup");

         }

 

         boolean customerWantsCondiments(){

                 return true;

         }

}

 

하위클래스(coffee)

public class CoffeeWithHook extends CaffeineBeverageWithHook{
         @Override
         void brew() {
                 // TODO Auto-generated method stub
                 System.out.println("Dripping Coffee through filter");
         }
 
         @Override
         void addCondiments() {
                 // TODO Auto-generated method stub
                 System.out.println("Adding Sugar and Milk");
         }
         @Override
         public boolean customerWantsCondiments() {
                 // TODO Auto-generated method stub
                 String answer = getUserInput();
 
                 if(answer.toLowerCase().startsWith("y")){
                          return true;
                 }else{
                          return false;
                 }
         }
 
         private String getUserInput(){
                 String answer = null;
 
                 System.out.println("would you like milk&sugar with your coffee (y/n) ?");
                 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
 
                 try {
                          answer = in.readLine();
                 } catch (Exception e) {
                          // TODO: handle exception1
                          System.err.println("IO error trying to read your answer");
                 }
 
                 if(answer==null){
                          return "no";
                 }
                 return answer;
         }
}

 

하위클래스(tea)

public class TeaWithHook extends CaffeineBeverageWithHook{
 
         @Override
         void brew() {
                 // TODO Auto-generated method stub
                 System.out.println("Steeping the tea");
         }
 
         @Override
         void addCondiments() {
                 // TODO Auto-generated method stub
                 System.out.println("Adding Lemon");
         }
         @Override
         public boolean customerWantsCondiments() {
                 // TODO Auto-generated method stub
                 String answer = getUserInput();
 
                 if(answer.toLowerCase().startsWith("y")){
                          return true;
                 }else{
                          return false;
                 }
         }
 
         private String getUserInput(){
                 String answer = null;
 
                 System.out.println("would you like lemon with your tea (y/n) ?");
                 BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
 
                 try {
                          answer = in.readLine();
                 } catch (Exception e) {
                          // TODO: handle exception1
                          System.err.println("IO error trying to read your answer");
                 }
 
                 if(answer==null){
                          return "no";
                 }
                 return answer;
         }
 
}

 

Main

public class BeverageTestDrive {
         public static void main(String[] args) {
                 TeaWithHook teaWithHook = new TeaWithHook();
                 CoffeeWithHook coffeeWithHook = new CoffeeWithHook();
                 
                 System.out.println("\n Making tea");
                 teaWithHook.prepareRecipe();
                 
                 System.out.println("\nMaking coffee");
                 coffeeWithHook.prepareRecipe();
         }
}

 

 

결과

Making tea

Boiling water

Steeping the tea

Pouring into cup

would you like lemon with your tea (y/n) ?

y

Adding Lemon

 

Making coffee

Boiling water

Dripping Coffee through filter

Pouring into cup

would you like milk &sugar with your coffee (y/n) ?

y

Adding Sugar and Milk



UML


'SW > DesignPattern' 카테고리의 다른 글

Composite Pattern (컴포지트 패턴)  (0) 2017.09.15
Iterator Pattern (반복자 패턴)  (0) 2017.09.14
Facade Pattern (퍼사드 패턴)  (0) 2017.09.07
Adapter Pattern (어댑터 패턴)  (0) 2017.09.07
Command Pattern (커맨드 패턴)  (0) 2017.09.07

Facade Pattern (퍼사드 패턴)

Facade Pattern


 

정의


어떤 sub-systeminterface에 대한 통합된 interface를 제공하고 facade에서 고수준 interface를 정의하기 때문에 sub-system을 쉽게 사용할 수 있다.



 

장점


더욱 더 간단한 interface를 만들 수 있다는 것이다


또한 client부와 sub-system을 분리할 수도 있다


만약 clientfacade로 만들었다고 하면 interface가 달라졌을 때 client는 변경할 필요 없이 facade만 변경하면 된다


그리고 표면에 드러나 있는 interface를 사용하고 그 내부는 알 필요가 없다.


 

l  최소지식원칙(=Law of Demeter)


system design에 있어서 어떤 object든 그 object와 상호작용하는 class의 수에 주의해야 하며, object들과 어떤 식으로 상호작용하는지도 주의해야 한다는 원칙이다.


위 원칙을 지키기 위해서는 4 종류의 object method만 호출하면 되는데, 다음과 같다.


1.    객체 자체


2.    Methodparameter로 전달된 object


3.    method에서 생성하거나 instance를 만든 object


4.    object에 속하는 구성요소 (A has B)




Adapter Pattern, Facade Pattern, Decorator Pattern의 차이점


l  Adapter Pattern : interface를 다른 interface로 변환하는 것이다. Interface를 변경해서 client에서 필요로 하는 interface로 적응시키는 위한 용도로 호환성을 위한다.


l  Facade Pattern : interface를 간단하게 바꾸는 것으로, 어떤 sub-system에 대한 간단한 interface를 제공하기 위한 용도로 사용되며 간편할 때 사용한다.


l  Decorator Pattern : interface를 바꾸지 않고 기능만 추가하는 것으로 Object를 감싸서 새로운 기능을 추가하기 위해서 사용한다.



 

정리


1.    기존 class를 사용하려고 하는데 interface가 맞지 않으면 adapter pattern을 사용한다.


2.    interface 또는 여러 interface를 단순화 시키거나 통합시켜야 한다면 facade pattern을 사용한다.


3.    Adapter Patterninterfaceclient가 원하는 interface로 바꾸는 역할을 하고 Facade Pattern은 복잡한 sub-system과 분리하는 역할을 한다.


4.    Adapter Pattern은 다중 Adapter로 만들 수 있고 Façade Pattern은 한 sub-system에 여러 개 만들 수 있다.

Adapter Pattern (어댑터 패턴)

Adapters Pattern

 

정의 : class interface client에서 필요로 하는 다른 interface 변환한다. Adapter 이용하면 interface 호환성문제로 없는 class들을 연결해서 있다.




그림과 같이 Adapter 소스만 변경하여 Existing System Vender Class 연결해 준다.

 


1.    Class Adapter Pattern


A.    장점 : Adapter 전체를 다시 구현할 필요가 없다.


B.     단점 : 상속을 활용하기 때문에 유연성이 떨어진다.
(java
다중상속 -지원 X)


C.    Class Adapter에서는 다중 상속을 이용하기 때문에 JAVA에서는 사용할 없다 (간접적 다중상속 지원 à Interface 이용)

 

2.    Object Adapter Pattern


A.    장점 : Composition 사용하기 때문에 유연성이 뛰어나다.


B.     단점 : Adapter Class 대부분의 코드를 구현해야 하기 때문에 효율성이 떨어진다.

 


사용방법


1.    Client에서 Target Interface 사용하여 method 호출함으로써 adapter 요청을 한다.


2.    Adapter에서는 adapter interface 사용하여 요청을 adapter 대한 하나 이상의 메소드를 호출로 변환한다.


3.    Client에서는 호출 결과를 받긴 하지만 중간에 Adapter 역할이 있었는지 전혀 없다.



기존의 Duck interface

public interface Duck {

         public void quack();

         public void fly();

}

 

Duck interface implement하는 MallardDuck

public class MallardDuck implements Duck{
    @Override
    public void fly() {
     // TODO Auto-generated method stub
     System.out.println("Quack");
    }
 
    @Override
    public void quack() {
     // TODO Auto-generated method stub
     System.out.println("I'm flying");
    }
}

 

하지만 turkey 오고싶다. 그래서 interface 만들어준다.

public interface Turkey {
    public void gobble();
    public void fly();
}

 

, Duck interface 사용하기 위해 Adapter 사용한다.

public class TurkeyAdapter implements Duck{
    Turkey turkey;
     public TurkeyAdapter(Turkey turkey){
     this.turkey=turkey;
    }
     @Override
    public void quack() {
     turkey.gobble();
    }         
    @Override
    public void fly() {
     turkey.fly();
    }
}


 

Turkey interface implements 하는 WildTurkey

public class WildTurkey implements Turkey{
    @Override
    public void fly() {
     // TODO Auto-generated method stub
     System.out.println("Gobble gobble");
    }
    @Override
    public void gobble() {
     // TODO Auto-generated method stub
     System.out.println("I'm flying a short distance");
    }
}

 

Main 소스

public class DuckTestDrive {
    public static void main(String[] args) {
     MallardDuck duck = new MallardDuck();
     WildTurkey turkey = new WildTurkey();
     Duck turkeyAdapter = new TurkeyAdapter(turkey);
     
     System.out.println("The turkey says...");
     turkey.gobble();
     turkey.fly();
     System.out.println("The Duck says...");
     testDuck(duck);
     System.out.println("The TurkeyAdapter says...");
     testDuck(turkeyAdapter);
    }
    
    public static void testDuck(Duck duck){
     duck.quack();
     duck.fly();
    }
}

 

결과 화면

The turkey says...

I'm flying a short distance

Gobble gobble

The Duck says...

I'm flying

Quack

The TurkeyAdapter says...

I'm flying a short distance

Gobble gobble



UML



Command Pattern (커맨드 패턴)

Command Pattern

 

정의

command pattern을 이용하면 요구사항을 객체로 캡슐화 하여, 요구사항을 나중에 이용할 수 있도록 메소드 이름과 매개변수 등 요구사항에 필요한 정보를 집어넣을 수 있다. 요청내역을 큐에 저장하거나 로그를 기록할 수 있으며, 작업취소 기능도 있다.

 

구성

1.    Client : Client ConcreteCommand를 생성하고 Receiver를 설정한다.

2.    Receiver : 요구사항을 수행하기 위해 어떤 일을 처리하는 객체

3.    Invoker : 명령이 있으며 execute() 메소드를 호출하여 커맨드 객체에게 특정 작업을 수행하도록 요청

4.    Command : 모든 커맨드 객체가 구현할 Interface. Receiver에게 시킬 모든 명령은 execute()메소드를 호출함으로써 수행되며, Receiver에게 특정 작업을 처리하라는 지시를 내린다.

5.    ConcreteCommand : 특정 행동과 Receiverbind한다. Invoker에서 execute()메소드 호출을 통해 요청하고 ConcreteCommand 객체에서는 Receiver에 있는 메소드를 호출함으로써 작업을 처리한다.


 

구현방법

1.    기능Class(Receiver)들을 캡슐화 한다.

2.    기능Class를 외부에 작동할 수 있는 Command (interface or class)를 추상화 및 구현한다.

3.    기능실행class(Invoker) command 타입 객체로 구현한다.

4.    기능실행class(Invoker) setter method를 구현하여 Command 타입 object를 가져오도록 한다.

5.    Invoker Object, Receiver Object, Command 타입의 Receiver Objectparameter로 하는 Object들을 생성한다.

6.    Invoker Object에서 Setter method를 호출하여 해당 commandAction Object를 등록하고, invoker에서 Command execute할 수 있는 method를 호출한다.

 

특징

request부와 execute부를 분리하고, undo, 보관, log생성이 가능하다.

장점

Receiver Command만 추가하면 dynamic하게 object 호출이 가능하다.

단점

Object 구성부가 추가되면 abstract부분부터 수정해야 한다.


 

Client 소스

public class RemoteLoader {

         public static void main(String[] args) {

                 RemoteControl remoteControl = new RemoteControl();

                 CeilingFan ceilingFan = new CeilingFan("Living Room");

                 CeilingFanHighCommand ceilingFanHigh = new CeilingFanHighCommand(ceilingFan);

                 CeilingFanOffCommand ceilingFanOff = new CeilingFanOffCommand(ceilingFan);

                 remoteControl.setCommand(2, ceilingFanHigh, ceilingFanOff);

                 System.out.println(remoteControl);

                 remoteControl.onButtonWasPushed(0);

                 remoteControl.offButtonWasPushed(0);

                 remoteControl.undoButtonWasPushed();

                 remoteControl.onButtonWasPushed(2);

                 remoteControl.offButtonWasPushed(2);

                 remoteControl.undoButtonWasPushed();

         }

}


Receiver 소스

/**
 * Receiver (천장의 FAN 작동시킨다)
 */
public class CeilingFan {
         public static final int HIGH=3;
         public static final int MEDIUM=2;
         public static final int LOW=1;
         public static final int OFF=0;
         String location;
         int speed;
         public CeilingFan(String location) {
                 // TODO Auto-generated constructor stub
                 this.location=location;
                 speed=OFF;
         }
         public void high(){
                 speed = HIGH;
         }
         public void medium(){
                 speed = MEDIUM;
         }
         public void low(){
                 speed = LOW;
         }
         public void off(){
                 speed = OFF;
         }
         public int getSpeed(){
                 return speed;
         }
}


 

Invoker

/**
 * Invoker
 */
public class RemoteControl {
         Command[] onCommands;
         Command[] offCommands;
         Command undoCommand;
         public RemoteControl() {
                 // TODO Auto-generated constructor stub
                 onCommands = new Command[7];
                 offCommands = new Command[7];
 
                 Command noCommand = new NoCommand();
                 for(int i=0; i<7; i++){
                          onCommands[i] = noCommand;
                          offCommands[i] = noCommand;
                 }
                 undoCommand = noCommand;
         }
         /**
          * @param slot
          * @param onCommand
          * @param offCommand
          * 리모컨의  slot command 넣는다.
          */
         public void setCommand(int slot, Command onCommand, Command offCommand) {
                 onCommands[slot] = onCommand;
                 offCommands[slot] = offCommand;
         }
         /**
          * @param slot
          * slot ON button 눌리면  slot OnCommand execute()메소드가 호출된다.
          */
         public void onButtonWasPushed(int slot) {
                 onCommands[slot].execute();
                 undoCommand = onCommands[slot];
         }
         public void offButtonWasPushed(int slot) {
                 offCommands[slot].execute();
                 undoCommand = offCommands[slot];
         }
         @Override
         public String toString() {
                 StringBuffer stringBuffer = new StringBuffer();
                 stringBuffer.append("\n-----Remote Control-----\n");
                 for(int i=0; i<onCommands.length; i++){
                          stringBuffer.append("[slot " + i + "] " + onCommands[i].getClass().getName()+"\n");
                 }
                 return stringBuffer.toString();
         }
         public void undoButtonWasPushed() {
                 // TODO Auto-generated method stub
                 undoCommand.undo();
         }
}


Command 소스 (fan의 속도를 높임)

/**
 *  class fan 속도를 높이고, Receiver CeilingFan 사이를 bind한다.
 */
public class CeilingFanHighCommand implements Command{
         CeilingFan ceilingFan;
         int prevSpeed;
 
         public CeilingFanHighCommand(CeilingFan ceilingFan) {
                 // TODO Auto-generated constructor stub
                 this.ceilingFan=ceilingFan;
         }
         
         /* 
          * Invoker에서 execute 호출하면 fan 속도를 높인다.
          * unDo 구현하기 위해 prevSpeed 속도 값을 저장해 둔다.
          */
         @Override
         public void execute() {
                 // TODO Auto-generated method stub
                 prevSpeed = ceilingFan.getSpeed();
                 ceilingFan.high();
         }
         
         /* 
          * prevSpeed 기준으로 speed 설정한다.
          */
         @Override
         public void undo() {
                 // TODO Auto-generated method stub
                 if(prevSpeed == CeilingFan.HIGH){
                          ceilingFan.high();
                 }else if (prevSpeed == CeilingFan.MEDIUM){
                          ceilingFan.medium();
                 }else if (prevSpeed == CeilingFan.LOW){
                          ceilingFan.low();
                 }else if (prevSpeed == CeilingFan.OFF){
                          ceilingFan.off();
                 }
         }
}


 

Command소스 (Fan의 속도를 설정)

public class CeilingFanOffCommand implements Command{
         CeilingFan ceilingFan;
         int prevSpeed;
 
         public CeilingFanOffCommand(CeilingFan ceilingFan) {
                 // TODO Auto-generated constructor stub
                 this.ceilingFan = ceilingFan;
         }
 
         @Override
         public void execute() {
                 // TODO Auto-generated method stub
                 prevSpeed = ceilingFan.getSpeed();
                 ceilingFan.off();
         }
 
         @Override
         public void undo() {
                 // TODO Auto-generated method stub
                 if(prevSpeed == CeilingFan.HIGH){
                          ceilingFan.high();
                 }else if(prevSpeed==CeilingFan.MEDIUM){
                          ceilingFan.medium();
                 }else if(prevSpeed==CeilingFan.LOW){
                          ceilingFan.low();
                 }else if(prevSpeed==CeilingFan.OFF){
                          ceilingFan.off();
                 }
         }
}

 

Command

/**
 *  class null object이다.
 * return object 없어도 client에서 null 처리하지 않아도
 * 되게   사용한다.
 */
public class NoCommand implements Command{
         @Override
         public void execute() {
                 // TODO Auto-generated method stub
 
         }
         @Override
         public void undo() {
                 // TODO Auto-generated method stub
 
         }
 
}


Interface

/**
 * 모든 command에서 구현해야 하는 interface이다.
 * 모든 command execute method 통해서 호출되며,
 *  method에서는 receiver 특정 작업을 처리하게 한다.
 */
public interface Command {
         public void execute();
         public void undo();
}

 


구현


흑백돌 옮기기 문제

문제


To solve the swapping puzzle, use only two types of moves. Move one stone into the empty hole or jump one stone over one stone of the opposite color (into the empty hole)

You may not back up, and you may not jump over two stones.


/*Visual Studio 2015*/
/*C++*/

#include<iostream>  
#pragma warning(disable:4996) //scanf warning 해제
using namespace std;
#define MAX 180
int n = 3, idx;
int res[MAX];//움직인 돌의 위치를 저장할 배열
char state[MAX];//현재 돌의 배열상태를 나타낼 배열
void init()//초기화
{
	idx = 1;//몇번 움직였는지 카운트 할 인덱스
	for (int i = 1; i <= n; i++)//1~n번째 돌은 흰색
		state[i] = 'W';
	state[n + 1] = ' ';//n+1번째는 빈칸
	for (int i = n + 2; i <= 2 * n + 1; i++)//n+2번째 부터 2n+1까지 검은돌
		state[i] = 'B';
}
void swap(int &x, int &y)//돌의 위치를 바꾸는 함수
{
	int t;
	t = x;
	x = y;
	y = t;
}

void sum_show()//움직인 돌의 위치를 보여주는 함수
{//총 20개가 넘으면 개행하도록 함
	int sum = 0;
	for (int i = 1; i <= n*(n + 2); i++)
	{
		if (sum % 20 == 0)
			printf("%d", res[i]);
		else printf(" %d", res[i]);
		sum++;
		if (sum % 20 == 0)
			printf("\n");
	}
	if (sum % 20)
		printf("\n");
}

void puzzle(int hole, int cnt)//hole은 빈칸의 위치, cnt는 움직인 횟수
{
	if (cnt == idx)
		idx++;
	if (hole - 2>0 && state[hole - 2] == 'W' && state[hole - 1] == 'B')
	{//빈칸의 왼쪽에 2개 이상의 돌이 있고, 빈칸에서 왼쪽으로 2칸은 W, 1칸은 B일때
		swap(state[hole - 2], state[hole]);//빈칸 왼쪽에서 두번째 자리에 위치한 돌을 빈칸으로 점프
		res[cnt] = hole - 2;//움직인 돌은 빈칸에서 왼쪽으로 두번째 있는 돌이므로, res에 해당 위치를 저장
		if (cnt == n*(n + 2) && hole - 2 == n + 1)//답이라면(cnt가 2^n+2n이고, 빈칸의 위치가 가운데일경우
		{
			sum_show();//결과를 보여주고
			return;//리턴
		}
		else if (cnt<n*(n + 2))//답보다 작다면
			puzzle(hole - 2, cnt + 1);//현재 빈칸의 위치는 원래 빈칸에서 왼쪽으로 두칸 이동하였다.
									//돌이 움직인 횟수가 1 증가인 상태로 재귀호출
		swap(state[hole - 2], state[hole]);
	}
	if (hole - 1>0 && state[hole - 1] == 'W')//빈칸의 왼쪽에 1개 이상의 돌이 있고, 바로 왼쪽의 돌은 W일때
	{
		swap(state[hole - 1], state[hole]);//왼쪽에 있는 돌을 빈칸으로 옮긴다.
		res[cnt] = hole - 1;//움직인 돌은 빈칸 왼쪽에 있던 돌이였으므로, res에 추가한다.
		if (cnt == n*(n + 2) && hole - 1 == n + 1)//답이라면 답을 보여주고 리턴
		{
			sum_show();
			return;
		}
		else if (cnt<n*(n + 2))//답보다 작다면
			puzzle(hole - 1, cnt + 1);//빈칸의 위치는 하나 밀렸고 이동횟수는 1 증가
		swap(state[hole - 1], state[hole]);
	}
	if (hole + 1 <= 2 * n + 1 && state[hole + 1] == 'B')
	{
		swap(state[hole + 1], state[hole]);
		res[cnt] = hole + 1;
		if (cnt == n*(n + 2) && hole + 1 == n + 1)
		{
			sum_show();
			return;
		}
		if (cnt<n*(n + 2))
			puzzle(hole + 1, cnt + 1);
		swap(state[hole + 1], state[hole]);
	}
	if (hole + 2 <= 2 * n + 1 && state[hole + 2] == 'B' && state[hole + 1] == 'W')
	{
		swap(state[hole + 2], state[hole]);
		res[cnt] = hole + 2;
		if (cnt == n*(n + 2) && hole + 2 == n + 1)
		{
			sum_show();
			return;
		}
		if (cnt<n*(n + 2))
			puzzle(hole + 2, cnt + 1);
		swap(state[hole + 2], state[hole]);
	}
}
void main()
{
	while (n != 0)//0을 입력받을때까지 루프를 돌림
	{
		printf("input n = ");
		scanf("%d", &n);//n을 입력받음
		init();
		puzzle(n + 1, 1);//인덱스를 1부터 시작하기 위해 n+1을 대입
		printf("result : %d\n\n", idx - 1);//1부터 시작하였기에 -1을 하여 올바른 결과가 나오도록 함
	}
}


'SW > Algorithm' 카테고리의 다른 글

카다네 알고리즘  (0) 2017.09.07

카다네 알고리즘

문제 : 정수로 이루어진 N x N 행렬이 있을때 사각형을 만들어 최대와 최소인 사각형을 만드는 알고리즘


출력 : 사각형의 좌측상단의 좌표, 우측하단의 좌표, 최댓값, 최솟값



/*Compiler : Visual Studio 2015 */
/*Language : C*/

#include <stdio.h>
#include <string.h>
#include <math.h>
#include <limits.h>
#include <stdlib.h>

#pragma warning(disable:4996)//scanf 경고 해제
#define MAX 1000

double calc(double* arr, int* first, int* last, int arr_size);
void findroot(double *arr[], int SizeOfArray);

int main()
{
	int SizeOfArray;
	double **arr;
	scanf("%d\n", &SizeOfArray);//행과 열의 크기를 먼저 받음
	arr = (double**)malloc(sizeof(double *)*SizeOfArray);
	for (int i = 0; i < SizeOfArray; i++)
	{
		arr[i] = (double *)malloc(sizeof(double)*SizeOfArray);
	}//동적배열 생성

	for (int i = 0; i < SizeOfArray; i++)
	{
		for (int j = 0; j < SizeOfArray; j++)
		{
			scanf("%lf", &arr[i][j]);
		}
	}//동적으로 받은 array에 데이터를 대입

	findroot(arr, SizeOfArray);
	//대입이 끝난 arr을 findroot에 배열의 행과 열의 크기와 함께 대입
	
	for (int i = 0; i<SizeOfArray; i++) 
	{
		free(arr[i]);
	}

	free(arr);	//array 동적할당 해제
	getchar();	//실행 후 종료됨 방지
	return 0;
}


void findroot(double *arr[], int SizeOfArray)
{
	int R_Left, R_Right, R_Up, R_Down, left, right, i, first, last;
	const int size = SizeOfArray;
	double maxSum = LONG_MIN, minSum = LONG_MIN, PartSum[MAX], sum;
	printf("\n%d x %d MATRIX\n", SizeOfArray, SizeOfArray);//행렬 정보 출력

	//Finding Maxvalue
	for (left = 0; left < SizeOfArray; ++left)
	{
		memset(PartSum, 0, sizeof(PartSum));
		for (right = left; right < SizeOfArray; ++right)
		{
			//left부터 right까지의 부분합을 구함
			for (i = 0; i < SizeOfArray; ++i)
			{
				PartSum[i] = PartSum[i] + arr[i][right];
			}
			sum = calc(PartSum, &first, &last, SizeOfArray);
			//1차원에서의 최소를 구함
			if (sum > maxSum)
			{
				maxSum = sum;
				R_Left = left;
				R_Right = right;
				R_Up = first;
				R_Down = last;
			}
		}
	}

	printf("\n(%d, %d) (%d, %d)\n", R_Up + 1, R_Left + 1, R_Down + 1, R_Right + 1);
	//첫번째 요소를 1,1로 잡기 위해 1씩 더해서 출력해줌
	printf("MaxSum is: %lf\n", maxSum);
	//printing maxvalue

	//Findidng minvalue
	for (int i = 0; i < SizeOfArray; i++)
	{
		for (int k = 0; k < SizeOfArray; k++)
		{
			arr[i][k] = (-1)*arr[i][k];//음수는 양수로 양수는 음수로 바꿔준다.
		}
	}

	//위의 maxvalue를 찾는것과 같은 코드
	for (left = 0; left < SizeOfArray; ++left)
	{
		memset(PartSum, 0, sizeof(PartSum));
		for (right = left; right < SizeOfArray; ++right)
		{
			//left부터 right까지의 부분합을 구한다.
			for (i = 0; i < SizeOfArray; ++i)
			{
				PartSum[i] += arr[i][right];
			}
			sum = calc(PartSum, &first, &last, SizeOfArray);
			//1차원에서의 최대를 구한다.
			if (sum > minSum)
			{
				minSum = sum;
				R_Left = left;
				R_Right = right;
				R_Up = first;
				R_Down = last;
			}
		}
	}
	minSum = (-1)*minSum; // 양, 음수를 바꿔계산한 결과를 다시 바꿔준다.
	printf("\n(%d, %d) (%d, %d)\n", R_Up + 1, R_Left + 1, R_Down + 1, R_Right + 1);
	printf("MinSum is: %lf\n", minSum);
	//printing minvalue
}

//1차원에서의 maxvalue를 구한다.
double calc(double* arr, int* first, int* last, int arr_size)
{
	double sum = 0, maxSum = LONG_MIN;
	int start_index=0, p=0;
	*last = -1; // 음수일 경우를 검사한다.

	for (int i = 0; i < arr_size; ++i)
	{
		sum += arr[i];
		if (sum < 0)
		{
			sum = 0;
			start_index = i + 1;
		}
		else if (sum > maxSum)
		{
			maxSum = sum;
			*first = start_index;
			*last = i;
		}
	}
	//음수를 검사한다.
	if (*last != -1)
		return maxSum;
	
	//모두 음수일때
	maxSum = arr[0];
	*first = 0;
	*last = 0;

	//가장 큰 원소를 찾는다. 
	for (int i = 1; i < arr_size; i++)
	{
		if (arr[i] > maxSum)
		{
			maxSum = arr[i];
			*first = i;
			*last = i;
		}
	}
	//test 모두 양수일때 minvalue test
	for(int i = 0; i < arr_size; i++)
	{
		for (int j = 0; j < arr_size; j++)
		{
			if (arr[i] >= 0)
				p = 1;
		}
	}
	if (p == 0)
		maxSum = arr[0];
	//test exit
	return maxSum;
}

자세한 설명은 주석을 참조하면 될것같다..


질문은 댓글주세요!

'SW > Algorithm' 카테고리의 다른 글

흑백돌 옮기기 문제  (0) 2017.09.07

'SW'에 해당되는 글 120건

1 ··· 3 4 5 6 7 8 9 ··· 12 →