클래스를 디자인하다 보면,

많은 경우 생성자의 인자를 통해 멤버변수를 초기화를 하는데,

멤버변수를 추가해야할 일이 생기게 되면,

덩달아서 생성자 또한 변경이나 추가를 해줘야 한다.

 

우선 Java를 예로 들어보자.

 

 

2개의 멤버변수를 갖는 클래스를 예로 들어보자.

public class TestClass

{

    private int m_iVal1 = 0;

    private int m_iVal2 = 0;

 

    public TestClass(int iVal1, int iVal2)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

    }

}

 

이 클래스에 멤버변수가 추가된다고 무작정 이렇게 클래스를 변경하는 사람은 없을꺼다.

public class TestClass

{

    private int m_iVal1 = 0;

    private int m_iVal2 = 0;

    private int m_iVal3 = 0;

 

    public TestClass(int iVal1, int iVal2, int iVal2)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

        m_iVal3 = iVal3;

    }

}

무작정 이렇게 변경을 해버리면

기존에 인자 2개 생성자 TestClass(1,2)를 사용했던 모든 프로그램들에선 오류가 발생한다.

 

그렇다고 이렇게 똑같은 생장자에 코드만 추가생성을 하면 어떻게 될까?

public class TestClass

{

    private int m_iVal1 = 0;

    private int m_iVal2 = 0;

    private int m_iVal3 = 0;

 

    public TestClass(int iVal1, int iVal2)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

    }

 

    public TestClass(int iVal1, int iVal2, int iVal3)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

        m_iVal3 = iVal3;

    }

}

이렇게 수정했을시 또다른 문제가 발생할 수 있다.

첫째 문제 불필요한 소스코드 작성이고,

둘째 문제는 TestClass(1, 2)와 TestClass(1,2,3)간 무결성 오류가 생길 수 있다는 것이다.

 

시간이 지나고 지나서, 생성자에서 멤버변수 초기화부분을 수정하게 된다면...

매번 TestClass(1, 2)와 TestClass(1,2,3)를 똑같이 수정해주거나,

한쪽에서 수정을 빼먹는다면 무결성 오류가 발생할 수 있는것이다.

 

따라서 이렇게 수정하는 것이 가장 깔끔하다고 할 수 있다.

public class TestClass

{

    private int m_iVal1 = 0;

    private int m_iVal2 = 0;

    private int m_iVal3 = 0;

 

    public TestClass(int iVal1, int iVal2)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

    }

 

    public TestClass(int iVal1, int iVal2, int iVal3)

    {

        this(iVal1, iVal2);    //이 코드앞에 다른 코드를 추가하면 컴파일 오류가 생긴다.

        m_iVal3 = iVal3;

    }

}

항상 주의해야 할 것은 생성자에서 또다른 생성자를 호출할때,

Body의 가장 첫부분에 생성자를 호출해야한다는 것이다.

생성자가 클래스를 초기화 하는 놈인데, 클래스 초기화도 전에 다른 것을 수행하면 안되기 때문이다.

아주 간단한 Print문이라도 예외없이 Java컴파일는 오류를 반환한다.

 

 

 

자 이번엔 C#을 예로 들어보자.

 

 

위와 똑같은 구분을 C#으로 작성해보면 에러가 발생한다.

public class TestClass

{

    private int m_iVal1 = 0;

    private int m_iVal2 = 0;

    private int m_iVal3 = 0;

 

    public TestClass(int iVal1, int iVal2)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

    }

 

    public TestClass(int iVal1, int iVal2, int iVal3)

    {

        this(iVal1, iVal2);    //여기서 오류가 발생한다.

        m_iVal3 = iVal3;

    }

}

C#에서는 생성자 Body안에서 생성자를 호출하는 것이 문법적으로 막혀있다.

Java에서는 Body안에 호출할 수는 있지만, 가장 처음에 호출하게 되었있는데,

C#에서는 아예 문법적으로 이를 제약하여 Body구문 이전에 이를 호출해야한다.

 

C#생성장에서 다른 생성자를 호출하려면 이렇게 해야한다.(C++을 써왔다면 익숙한 구문)

public class TestClass

{

    private int m_iVal1 = 0;

    private int m_iVal2 = 0;

    private int m_iVal3 = 0;

 

    public TestClass(int iVal1, int iVal2)

    {

        m_iVal1 = iVal1;

        m_iVal2 = iVal2;

    }

 

    public TestClass(int iVal1, int iVal2, int iVal3) : this(iVal1, iVal2)

    {

        m_iVal3 = iVal3;

    }

}


Google AdSense

지난밤에 잘려고 누웠다가 뜬금없는 생각이 들었다.

Interface를 구현한 Class에서 같은 이름의 변수를 재정의 하면 어찌될까?

원래 알았던거 같은데, 긴가민가한 문제 ^^;;




Interface 하나와 그를 구현한 Class 하나를 만들고 테스트 해보았다.

먼저 작성한 Interface와 Class

interface INagarryInterface

{

    String I_NAME = "ITestInterface";

    String GetClassName();

}


class NagarryClass implements INagarryInterface

{

    public static final String I_NAME = "TESTClass";

    public String GetClassName()

    {

       return I_NAME;

    }

}


interface와 class모두 맴버로 I_NAME이란 변수와 GetClassName이라는 함수가 정의 되어있다.

그럼 이를 사용해서 아래와 같이 실험해보자

public class AppMain

{

    public static void main(String[] args)

    {

        System.out.println(INagarryInterface.I_NAME); //(1)

        System.out.println(NagarryClass.I_NAME); //(2)

        

        INagarryInterface test1 = new NagarryClass();

        System.out.println(test1.I_NAME); //(3)

        System.out.println(test1.GetClassName()); //(4)

        

        NagarryClass test2 = new NagarryClass();

        System.out.println(test2.I_NAME); //(5)

        System.out.println(test2.GetClassName()); //(6)

    }

}



총6번의 출력이 있다.

(1)번의 출력결과야 보나마나

ITestInterface

이고, (2)번 출력결과야 보나마나

TestClass일 것 이다.


결과값은 다음과 같다.

ITestInterface

TESTClass

ITestInterface

TESTClass

TESTClass

TESTClass



(3)번과 (4)번의 출력결과가 재미지다.

똑같은 test1이라는 객체를 이용해서 I_NAME 변수를 출력한것인데,

결과가 다르니 말이다.


(3)번의 이유는 이렇다.

INagarryInterface test1 = new NagarryClass();

이라고 선언했으니....

test1은 NagarryClass의 Heap메모리를 갖게되지만,

엄연히 INagarryInterface타입이다.


I_NAME 변수는 인터페이스나 클래스에서 static으로 정의 되어있다.

(인터페이스의 변수는 무조껀 public static final이다. public static final을 명시하든 하지않든 말이다.) 

test1이 인스턴스 객체이지만, I_NAME은 Heap영역에 있는 것이 아니라,

static 데이터 영역에 있다.

따라서 test1의 I_NAME에 접근하게 되면 NagarryClass이 아닌

INagarryInterface에 접근하게 되는 것이다.

test1는 INagarryInterface타입이니 말이다.


(4)번의 이유는 반대로...

test1의 함수 GetClassName()를 이용하여 I_NAME 변수에 접근했는데,

이 함수는 Heap영역에 있고, 이건 명시적인 NagarryClass에 접근하게 되는것이다.

따라서 이 함수안에서 접근한 I_NAME는 INagarryInterface의 데이터 영역이 아니라 아니라 NagarryClass영역이다.



결과 인증샷



Google AdSense

간단하게... 로그파일 남기는  클래스를 만들어 보자.



작성할 로그파일기록 클래스는....
아래의 3가지 원칙을 지키는 것으로 하자.

1. 날짜별 로그파일 생성 : 로그파일은 1일 1건으로 생성시킨다.
2. 로그가 기록되는 시간을 표시한다.
3. 멀티쓰레드 환경에서 안전하게 동작한다.

1. 파일명은...
예를 들어 오늘이 2012년01월13일 이니까
"20120113_Log.Log"
뭐 이런식으로 생성하기로 하자.

2. 파일속 로그내용은...
"무궁화 꽃이 피었습니다."라는 내용을 로그에 남길때는
"[2012-01-13 17:04:59]:무궁화 꽃이 피었습니다."라고 남기면 되겠다.

3. 멀티쓰레드환경에서 안전하게 작동해야 하니까
대충 함수를 static synchronized로 만들어버리자.



이렇게 해서 클래스를 짜보면....
이렇게 나오겠네

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class FileLog {
    public static synchronized void writelog(String sLog)
    {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        SimpleDateFormat formatter2 = new SimpleDateFormat("yyyyMMdd");
        String sToday = formatter.format(cal.getTime()); //for log-time
        String sDate = formatter2.format(cal.getTime()); //for file-name

        String sFileName = sDate +"_Log.Log";
        sLog = "[" + sToday + "]:" + sLog;

        try
        {
            BufferedWriter bw = new BufferedWriter(new FileWriter(sFileName, true));
            bw.write(sLog);
            bw.newLine();
            bw.close();
        }
        catch(IOException ie)
        {
            ie.printStackTrace();
        }
    }
}



추가적으로 고려할게 있는데...
현재는 하루1건의 로그파일만 생성하는데,
경우에 따라 하루에 로그가 100MB이상도 쌓이는 프로그램이 있다.
그럴때는 파일용량이 일정수준을 넘으면 새로은 로그파일을 만들어 쓰는게 좋다.
하루에 100MB가 쌓인다면 10MB정도로 잘라서 10개 정도를 생성시키는 것이 좋지 않겠는가~

그렇게 할려면 아래의 2가지 정도를 추가해주면 되겠다.
첫째, 마지막에 생성된 파일의 용량을 체크하는 로직 추가
둘째, 파일이름에 시간이나 순번을 넣어서 파일명이 겹치지 않게 처리하는 구문 추가

하지만 귀찮아서 그냥 여기까지.... ㅡ,.ㅡ;;
Google AdSense

+ Recent posts