프로그래머라면 누구나 한 번쯤은 안드로이드 프로그래밍을 생각해 보거나 또 직접 수행해 봤을 것이다. 특히 국내의 경우 자바 개발자가 많아 특히, JEE 개발을 많이 해본 사람들을 중심으로 안드로이드 프로그래밍을 선택하는 것을 많이 봤다. 또 대학생이나 자바를 전혀 배우지 않은 일반 사람들도 안드로이드 앱을 개발하려는 모습을 심심치 않게 볼 수 있다.
필자는 자신이 구상하고 있는 안드로이드 앱이 탄탄하게 구성되려면, 반드시 자바 핵심 기술에 대한 이해가 필수적이라고 강조한다. 따라서 이번 시간에는 알고리즘과 디자인 패턴을 적용한 자바 핵심 요소들로 기본적인 프로그래밍을 완성시켜 보고, 다음 시간에는 안드로이드 프로그래밍의 핵심이라고 할 수 있는 UI를 핸들링하는 방법을 살펴보겠다.
이어 웹앱을 이용해 커뮤니티 연동 앱을 한번 만들어보는 시간을 갖겠다. 만일 안드로이드 OS 셋팅과 에뮬레이터 작동에 대해 알고 있다면, 좀 더 빠른 시간 안에 안드로이드 앱을 구현할 수 있게 될 것이다.
필자는 자신이 구상하고 있는 안드로이드 앱이 탄탄하게 구성되려면, 반드시 자바 핵심 기술에 대한 이해가 필수적이라고 강조한다. 따라서 이번 시간에는 알고리즘과 디자인 패턴을 적용한 자바 핵심 요소들로 기본적인 프로그래밍을 완성시켜 보고, 다음 시간에는 안드로이드 프로그래밍의 핵심이라고 할 수 있는 UI를 핸들링하는 방법을 살펴보겠다.
이어 웹앱을 이용해 커뮤니티 연동 앱을 한번 만들어보는 시간을 갖겠다. 만일 안드로이드 OS 셋팅과 에뮬레이터 작동에 대해 알고 있다면, 좀 더 빠른 시간 안에 안드로이드 앱을 구현할 수 있게 될 것이다.
마방진으로 보는 자바 핵심 기술프로그래밍의 기본은 알고리즘이다. 알고리즘을 쉽게 설명하기 위해 필자는 그림을 먼저 제시하고, 해당 그림의 소스 코드를 설명하는 식으로 진행하겠다.
<그림 1>은 마방진으로 자바의 핵심 기술을 표현한 것이다.
<그림 1>은 마방진으로 자바의 핵심 기술을 표현한 것이다.
<그림 1> 마방진으로 자바 핵실 기술 익히기
우선 마방진이란 무엇인지 알아보자. 마방진이란 기원전부터 전쟁에서 사용하던 진법 중 하나로, 동일한 인원으로 상대방이 어떤 방향에서 공격해도 방어할 수 있다는 특징을 가지고 있다.
수학에서는 이 마방진을 ‘2차원 배열형태 값에서 가로, 세로, 대각선 중 어느 곳으로 더해도 일정한 값이 나오게 하는 방법’이라고 정의하며, 크게 홀수와 짝수(일반항이 4*n , 4*n+2) 형태로 나눈다.
수학에서는 이 마방진을 ‘2차원 배열형태 값에서 가로, 세로, 대각선 중 어느 곳으로 더해도 일정한 값이 나오게 하는 방법’이라고 정의하며, 크게 홀수와 짝수(일반항이 4*n , 4*n+2) 형태로 나눈다.
<그림 2> 마방진의 정의와 종류
먼저 홀수 마방진에 대한 로직을 살펴보자.
<그림 3> 홀수 마방진과 알고리즘
<리스트 1>과 같이 작성한 뒤 테스트해 보면, 실제 적용된 소스가 무엇인지 확인할 수 있다. 확인 방법은 <리스트 2>와 같다.
<리스트 1> 홀수 마방진 소스 : OddMagicSquare.java
package kr.or.javacafe.magic; public class OddMagicSquare { protected int[][] magic; // 전체 2차원 배열을 구성하기 위한 변수 protected int top; // x-1 < 0 or y-1 < 0 일 경우 // x = n-1 or y = n-1을 넣어주기 위한 값 public OddMagicSquare(){ // 생성자 : 객체가 생성될 때 한번만 호출되는 것 this(3); // 홀수 마방진이므로 생성자에 argument가 없어도 // 홀수 마방진의 시작인 3으로 값을 초기화 } public OddMagicSquare(int n){ // argument가 있는 객체 생성 시 this.init(n); // 값을 초기화 하는 init() 메소드 호출 } public void init(int n){ // 객체 생성 시 입력받은 값으로 값을 초기화 this.magic = new int[n][n]; this.top = n-1; } public void make(){ // OddMagicSquare 로직 구현!! int x = 0; int y = top/2; magic[x][y] = 1; for (int i = 2; i <= (top+1)*(top+1) ; i++) { int tempX = x; int tempY = y; if (x-1<0) { x = top; }else{ x--; } if (y-1<0) { y = top; }else{ y--; } if (magic[x][y]!=0) { x = tempX + 1; y = tempY; } magic[x][y]=i; } }
public void print(){ // 만들어진 OddMagic Square 출력
for (int i = 0; i < magic.length; i++) { for (int j = 0; j < magic[i].length; j++) { System.out.print(magic[i][j]+"\t"); } System.out.println(); } }
public boolean isCheck(){ // 마방진이 제대로 만들어졌는지 확인하는 메소드
boolean isCheck = true; int count = top+1; int[] mcheck = new int[2*count+2]; // 검증해야 할 값 전체 // 가로 n개, 세로 n개, 대각선 2개 for (int i = 0; i < magic.length; i++) { for (int j = 0; j < magic[i].length; j++) {
// 가로 mcheck[0] = magic[0][0]+magic[0][1]+magic[0][2]
mcheck[i] += magic[i][j];
// 세로 mcheck[3] = magic[0][0]+magic[1][0]+magic[2][0]
mcheck[i+count] += magic[j][i];
// ‘\’ 대각선 mcheck[6] = magic[0][0]+magic[1][1]+magic[2][2]
if (i==j) { mcheck[2*count] += magic[i][j]; } // '/' 대각선 mcheck[6] = magic[0][0]+magic[1][1]+magic[2][2] if (i+j==count-1) { mcheck[2*count+1] += magic[i][j]; } } }
// mcheck [2*count+2]의 방에 있는 값 중 하나라도 틀리면
// false를 리턴 !! for (int i = 1; i < mcheck.length; i++) { if (mcheck[0]!=mcheck[i]) { isCheck = false; break; } } return isCheck; }// isCheck }// class |
<리스트 2> 홀수 마방진 확인 소스 : MagicMain.java
package kr.or.javacafe.magic; public class MagicMain{ public static void main(String[] args){ OddMagic om = new OddMagic(); // 객체생성 om.make(); // 2차원 배열 만들기 om.print(); // 만들어진 2차원 배열 출력하기 System.out.println(om.isCheck()); // 만들어진 홀수 마방진이 맞는지 확인 // true or flase } } |
홀수 마방진을 살펴봤으니 이제 짝수 마방진인 4 마방진과 6 마방진을 알아보겠다. 아마 4 마방진과 6 마방진을 알아보고 나면 공통적으로 사용되는 메소드와 그렇지 않은 메소드가 무엇인지를 알 수 있게 될 것이다. 이를 통해 OOP(Object Oriented Programming)를 적용할 때 클래스를 나누는 방법과 패턴을 적용하는 방법 중 어떤 것이 더 좋은지 파악할 수 있게 된다.
4 마방진은 크게 두 가지로, 기본 흐름과 역 흐름이 있다. 우리는 이 두 가지 알고리즘을 메소드로 구현해 보겠다. 기본 흐름은 1~16(n)까지의 숫자를 순서대로 넣어주면 되고, 역 흐름은 기본 흐름과 반대로 1~16까지의 숫자를 거꾸로 넣으면 된다.
그러나 역 흐름은 특정 영역에만 적용시켜야 한다. 역 흐름을 적용하려면 <그림 4>와 같이 x와 y로 영역을 나눠 해당 값을 대입하는 것이 핵심 포인트다. 그래서 먼저 x 범위를 구한 뒤 y 영역을 구하고, 두 범위가 겹치는 부분에 넣고자 하는 값을 역순으로 입력하면 역 흐름 4 마방진이 완성된다.
<그림 4>, <그림 5>, <그림 6>을 보면 4 마방진에 대한 내용을 쉽게 이해할 수 있을 것이다.
<그림 4> 4 마방진 1단계
<그림 5> 4 마방진 2단계
<그림 6> 4 마방진 3단계
이를 완성하면 <리스트 3>과 같은 로직을 구할 수 있다.
<리스트 3> 4 마방진 소스 : FourMagicSquare.java
package kr.or.javacafe.magic; public class FourMagicSquare{ // 공통적으로 구현이 적용되는 부분 public FourMagicSquare(){ this(4); }// 생성자에 argument를 넣지 않아도 기본값 4를 입력 public FourMagicSquare(int n){} // 생성자에 argument를 입력하면 호출되는 생성자 public void init(int n){} // 값을 초기화 public void print(){} // 4 마방진 출력 public boolean isCheck(){} // 4 마방진 검증
// 4 마방진 로직구현
public void make(){ makeRight(); makeLeft(); }
// 4 마방진 기본 흐름 순서대로 1~ 16까지 숫자 넣기
public void makeRight() { int count = top+1; for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { magic[i][j]+=i*count+j+1; } } }
// 4 마방진의 역 흐름의 조건을 찾아 원하는 숫자 넣기
public void makeLeft() { int count = top+1; for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { if( (i>=0 && i }//if }else{ if ((j>=0 && j }//if }// if~else }//inner for }// outter for }// makeLeft } //class |
4 마방진에 이어 6 마방진을 살펴보겠다. 6 마방진의 로직은 조금 복잡한 편이다. 그래서 그림을 먼저 보고 이해하는 것이 좋다(<그림 7>, <그림 8> 참조).
<그림 7> 6 마방진 1단계
<그림 8> 6 마방진 2단계
이를 토대로 6 마방진을 구현한 것이 <리스트 4>이다.
<리스트 4> 6 마방진 소스 : SixMagicSquare.java
package kr.or.javacafe.magic; public calss SixMagicSquare{ // 공통적으로 구현이 적용되는 부분 public SixMagicSquare(){ this(6); }//생성자에 argument를 넣지 않아도 기본값 6을 입력 public SixMagicSquare(int n){} // 생성자에 argument를 입력하면 호출되는 생성자 public void init(int n){} // 값을 초기화 public void print(){} // 6 마방진 출력 public boolean isCheck(){} // 6 마방진 검증
// 6 마방진 로직 구현하는 메소드
public void make(){ makeA(); // A 영역 만들기 makeB(); // B 영역 만들기 makeCD(); // A:0->3, 3->0으로 변환해 C로, B:1->2. 2->1로 변환해 D로 multiples(); // A, B, C, D 각 영역에 n/2 * n/2를 곱한다. addABCD(); // OddMagicSquare(n/2)로 만들어진 값을 A, B, C, D에 더한다. }
public void makeA() {
int count = top+1; for (int i = 0; i < count/2; i++) { for (int j = 0; j < count/4; j++) { if (i==count/4) { magic[i][j+1]=3; }else{ magic[i][j]=3; } } } }// makeA
public void makeB() {
int count = top+1; for (int i = 0; i < count/2; i++) { for (int j = 0; j < count/2; j++) { magic[i][j+count/2]=1; } } for (int i = 0; i < count/2; i++) { for (int j = 0; j < count/2-(count/4-1); j++) { magic[i][j+count/2]=2; } } }// makeB
public void makeCD() {
int count = top+1; for (int i = 0; i < count/2; i++) { for (int j = 0; j < count/2; j++) { if (magic[i][j]==0) { magic[i+count/2][j]=3; }else if(magic[i][j]==3){ magic[i+count/2][j]=0; } if(magic[i][j+count/2]==2){ magic[i+count/2][j+count/2]=1; }else if (magic[i][j+count/2]==1) { magic[i+count/2][j+count/2]=2; } } } }//makeCD
public void multiples() {
int count = top +1; for (int i = 0; i < count; i++) { for (int j = 0; j < count; j++) { magic[i][j] *= count/2 * count/2; } } }// multiples
public void addABCD() {
int count = top+1; OddMagicSquare odd = new OddMagicSquare(count/2); odd.make(); int[][] oddM = odd.getMagic(); for (int i = 0; i < count/2; i++) { for (int j = 0; j < count/2; j++) { magic[i][j] += oddM[i][j]; magic[i][j+count/2] += oddM[i][j]; magic[i+count/2][j] += oddM[i][j]; magic[i+count/2][j+count/2] += oddM[i][j]; } } }// addABCD }//class |
이렇게 만들어진 클래스를 클래스 다이어그램(Class Diagram) 형태로 나타낸 것이 <그림 9>와 <그림 10>이다.
<그림 9> 홀수 마방진 클래스 다이어그램
<그림 10> 4 마방진과 6 마방진 클래스 다이어그램
6 마방진까지 구현할 수 있다면 3부터 시작해 어떤 수를 넣어도 마방진을 이룰 수 있는 클래스를 만들 수 있다. 지금까지 살펴본 홀수 마방진, 4 마방진, 6 마방진을 연계시켜 만능 마방진을 만들어 보겠다. 만능 마방진을 만드는 단계는 다음과 같다.
1. abstract class 구현(공통적인 부분처리)
2. 계층 구조(hierarchy) 만들기
3. 인터페이스 선언
4. 팩토리 패턴(factory pattern)을 적용해 진행
abstract class 구현(공통적인 부분처리)을 어떻게 구성할지 그림으로 살펴보겠다(<그림 11>, <그림 12> 참조).
2. 계층 구조(hierarchy) 만들기
3. 인터페이스 선언
4. 팩토리 패턴(factory pattern)을 적용해 진행
abstract class 구현(공통적인 부분처리)을 어떻게 구성할지 그림으로 살펴보겠다(<그림 11>, <그림 12> 참조).
<그림 11> 만능 마방진 만들기 1단계
<그림 12> 만능 마방진 만들기 2단계
앞서 만들어 본 ‘OddMagicSquare’, ‘FourMagicSquare’, ‘SixMagicSquare’ 소스에는 공통적으로 ‘void init(int n)’, ‘void print()’, ‘boolean isCheck()’이 들어있는 걸 알 수 있다. 이 메소드는 어디서든 하는 일이 똑같기 때문에, 각 클래스에서도 그 역할 그대로 수행하고 있다. 그래서 이 소스를 담아두는 클래스를 하나 만들어 상속받게 했다.
또 void make() 메소드는 공통적으로 들어가지만 각 클래스 별로 로직이 다르게 구현되기 때문에, abstract class에서 선언만 하고 구현하지는 않았다. 왜냐하면 abstract class는 abstract 메소드 즉, 구현되지 않고 선언만 되는 메소드가 하나 이상 있어야 되기 때문이다(<리스트 5> 참조).
또 void make() 메소드는 공통적으로 들어가지만 각 클래스 별로 로직이 다르게 구현되기 때문에, abstract class에서 선언만 하고 구현하지는 않았다. 왜냐하면 abstract class는 abstract 메소드 즉, 구현되지 않고 선언만 되는 메소드가 하나 이상 있어야 되기 때문이다(<리스트 5> 참조).
<리스트 5> 공통적인 코드 적용 소스 : Magic.java
abstract class Magic{ public int top; public int[][] magic; public void init(int n){}; // 구현 public void print(){}; // 구현 public boolean isCheck(){}; // 구현 public abstract void make(); // 선언만 } |
다음은 계층 구조(hiearachy) 만들기다.
우리는 <리스트 6>에서 계층 구조를 만들기 위해 꼭 필요한 내용들로만 구성했다는 점을 확인할 수 있다. 공통적으로 적용되는 메소드나 멤버 필드는 미리 구현한 클래스를 가져다 사용하면 되기 때문이다.
<리스트 6> 계층 구조 소스 : Magic.java
class OddMagicSquare extends Magic{ public OddMagicSquare(){ this(3); } public OddMagicSquare(int n){ super.init(n); } public void make(){ // 원소스 그대로 적용 } } class FourMagicSquare extends Magic{ public FourMagicSquare(){ this(4); } public FourMagicSquare(int n){ super.init(n); } public void make(){ // 원소스 그대로 적용 } } class SixMagicSquare extends Magic{ public SixMagicSquare(){ this(6); } public SixMagicSquare(int n){ super.init(n); } public void make(){ // 원소스 그대로 적용 } } |
이 경우 메소드가 하나라도 없으면 올바르게 동작하지 않기 때문에 그 부분을 강요하는 인터페이스를 선언해 보겠다. 인터페이스는 내용이 안에 포함되는 것이 아니기 때문에 간단하게 선언만 하는 형태로 구성한다.
<리스트 7> 인터페이스 소스 : IMagic.java
interface IMagic{ public void init(int n); public void make(); public void print(); public boolean isCheck(); } |
인터페이스로 선언된 내용을 상속(implements) 받으면 반드시 구현해야 한다. 따라서 꼭 필요한 메소드만 인터페이스로 선언하면 된다. 이 때 계층 구조를 만들고 싶다면 다음 소스를 추가하면 된다.
abstract class Magic implements IMagic { ... }
이 소스를 추가할 때 꼭 필요한 메소드인 경우 인터페이스에 선언(IMagic)한다. 이를 상속 받은 abstract class(Magic)는 공통적인 것은 구현하지만, 다시 구현해야 하는 부분은 선언만 한다. 최종적으로 상속하는 홀수(Odd)나 짝수(Four, Six) 마방진에서 로직만 구현하면, 어떠한 숫자를 대입해도 마방진을 구성할 수 있게 된다.
하지만 특정 숫자를 넣었을 때 그 숫자에 해당하는 마방진이 만들어지게 하려면 클래스를 하나 더 생성해야 한다. 다시 말해 메소드 하나를 호출시켜 원하는 숫자에 따라 홀수 또는 4, 6 마방진이 만들어지도록 해야 한다.
이를 위해 팩토리 패턴을 사용한다. 팩토리 패턴은 하나의 재료를 넣으면 일정한 기준에 따라 제품으로 만드는 패턴이다. 물론 반드시 팩토리 패턴을 사용하지 않아도 된다. 하지만 검증된 패턴을 사용하는 것이 가장 효과적이다. 팩토리 패턴까지 이용해 완성시킨 만능 마방진 소스가 <리스트 8>이다.
하지만 특정 숫자를 넣었을 때 그 숫자에 해당하는 마방진이 만들어지게 하려면 클래스를 하나 더 생성해야 한다. 다시 말해 메소드 하나를 호출시켜 원하는 숫자에 따라 홀수 또는 4, 6 마방진이 만들어지도록 해야 한다.
이를 위해 팩토리 패턴을 사용한다. 팩토리 패턴은 하나의 재료를 넣으면 일정한 기준에 따라 제품으로 만드는 패턴이다. 물론 반드시 팩토리 패턴을 사용하지 않아도 된다. 하지만 검증된 패턴을 사용하는 것이 가장 효과적이다. 팩토리 패턴까지 이용해 완성시킨 만능 마방진 소스가 <리스트 8>이다.
<리스트 8> 완성된 만능 마방진 소스
package kr.or.javacafe.magic; public class MagicFactory { public static IMagic imagic; public static IMagic factory(int n){ // n이라는 어떤 숫자를 넣는다. if (n%2!=0) { // n이 홀수이면 OddMagicSquare가 실행 imagic = new OddMagicSquare(n); }else if(n%4==0){ // n이 4의 배수이면 FourMagicSquare가 실행 imagic = new FourMagicSquare(n); }else{ // n이 홀수도 아니고 4의 배수도 아니면 SixMagicSquare가 실행 imagic = new SixMagicSquare(n); } return imagic; } }// class |
<그림 13> 만능 마방진 만들기 3단계
만들어진 만능 마방진을 사용해 보자.
<리스트 9> 만능 마방진 확인 소스
package kr.or.javacafe.magic; class MagicMain{ IMagic imagic = MagicFactory.factory(10); imagic.make(); imagic.print(); System.out.println(imagic.isCheck()); } |
지금까지 어떤 수를 넣어도 마방진을 생성할 수 있는 프로그램을 작성해 봤다. 프로그램은 로직과 데이터의 결합 형태로 작성된다. 따라서 로직이 명확하게 정리돼 있어야, 내가 사용하는 데이터를 가공해 원하는 목적을 수행시킬 수 있다.
정리하며
무엇이든 기본이 전제 된 다음에 지금 유행하는 프레임워크나 패턴을 적용시켜야 좀 더 좋은 결과를 얻을 수 있다. 안드로이드 프로그래밍도 마찬가지다. 자바에 대한 기본기가 탄탄하다면 좀 더 쉽고 안드로이드 플랫폼에 최적화된 프로그램을 만들 수 있다. 다음 시간에는 UI와 이벤트 핸들링에 대해 알아보겠다. 안드로이드 프로그래밍이 즐거운 코딩 작업이 되길 바란다.
무엇이든 기본이 전제 된 다음에 지금 유행하는 프레임워크나 패턴을 적용시켜야 좀 더 좋은 결과를 얻을 수 있다. 안드로이드 프로그래밍도 마찬가지다. 자바에 대한 기본기가 탄탄하다면 좀 더 쉽고 안드로이드 플랫폼에 최적화된 프로그램을 만들 수 있다. 다음 시간에는 UI와 이벤트 핸들링에 대해 알아보겠다. 안드로이드 프로그래밍이 즐거운 코딩 작업이 되길 바란다.
No comments:
Post a Comment