언어 자료구조 알고리즘/Escort C#

[C#] 7.4 프로젝트 구현 - 초기화

언제나휴일 2016. 5. 3. 13:02
반응형

7.4 프로젝트 구현

 

초기화



초기화 - 캠퍼스와 장소 생성 시퀀스 다이어그램

[그림] 초기화 - 캠퍼스와 장소 생성 시퀀스 다이어그램


초기화 - 학생 생성 시퀀스 다이어그램

[그림] 초기화 - 학생 생성 시퀀스 다이어그램


 이제는 시나리오와 시퀀스 다이어그램 등을 보면서 구체적으로 구현해 보기로 합시다.

 

 시나리오를 보시면 캠퍼스 생활은 크게 초기화 부분과 사용자에 의한 동작으로 나눌 수가 있습니다. 이에 캠퍼스 생활에는 초기화하는 Init 메서드와 사용자에 의한 동작인 Run 메서드를 추가하고 프로그램 진입점에서는 캠퍼스 생활 단일체를 참조하여 Init Run 메서드를 호출하기로 합시다.

 

class Program

{

    static void Main(string[] args)

    {

        CampusLife campuslife = CampusLife.Singleton;

        campuslife.Init();

        campuslife.Run();

    }

}

 

class CampusLife

{

    ... 중략 ...

    internal void Init()

    {

        throw new System.NotImplementedException();

    }

    internal void Run()

    {

        throw new System.NotImplementedException();

    }

}

  

 현 상태에서 빌드해 보면 Place가 추상 클래스로 되어 있지 않다는 오류를 확인할 수 있습니다. 여러분은 단계마다 습관적으로 빌드하여 문법적 오류가 있는 부분을 계속 수정하시기 바랍니다.

 

abstract class Place

{

    ... 중략 ...

}

 

 캠퍼스 생활의 초기화 부분을 작성해 보기로 합시다. 초기화 과정에서는 각 장소를 생성하는 부분과 학생을 생성하는 부분으로 나눌 수 있습니다. 먼저, 각 장소를 생성하는 부분에서는 Campus 개체를 생성과 각 장소들을 생성하는 부분이 있습니다. 프로그램의 시나리오를 변경하여도 수정하기 쉽게 하려고 각 장소 유형을 열거형으로 정의하여 이용하기로 할게요.

 

enum PlaceType

{

    PT_LECTUREROOM,

    PT_DORMITORY,

    PT_LIBARY,

    MAX_PLACETYPES

}

  

 캠퍼스 생활에서 초기화를 담당하는 Init 메서드에서는 장소를 생성하는 부분과 학생들을 생성하는 부분이 있으므로 이들을 별도의 메서드로 정의하여 Init 메서드에서는 이들을 호출하는 것으로 구현하기로 할게요.

 

class CampusLife

{

    ... 중략 ...

    internal void Init()

    {

        MakePlaces();

        MakeStudents();

    }

    private void MakeStudents()

    {

        throw new System.NotImplementedException();

    }

    private void MakePlaces()

    {

        throw new System.NotImplementedException();

    }

}

 

 각 장소를 생성하는 MakePlaces를 구현해 봅시다. 여기에서는 캠퍼스 개체를 생성하는 것과 각 장소를 구현하는 것을 담당합니다. 그리고 캠퍼스 생활에서는 생성한 캠퍼스 개체와 각 장소 개체들을 이용해야 하므로 멤버 필드를 선언하기로 합시다. 이에 캠퍼스 개체는 하나만 생성할 것이므로 Campus 형식의 멤버 필드를 선언하고 각 장소는 Place 형식을 원소로 하는 배열로 선언합시다. 그리고 Init 메서드에서 Campus 개체와 각 장소 개체를 생성하여 적절한 멤버 필드에 대입해 주면 되겠죠.

 

class CampusLife

{

    ... 중략 ...

    Place[] places = new Place[(int)PlaceType.MAX_PLACETYPES];

    Campus campus;

 

    private void MakePlaces()

    {

        campus = new Campus();

        MakePlace(PlaceType.PT_LECTUREROOM, new LectureRoom());

        MakePlace(PlaceType.PT_DORMITORY, new Dormitory());

        MakePlace(PlaceType.PT_LIBARY, new Library());

    }

 

    private void MakePlace(PlaceType placeType, Place place)

    {

        places[(int)placeType] = place;

   }

}

  

 그리고 Campus 생성과 각 장소 생성자에서는 특별한 행위가 약속된 바가 없으니 예외 구문을 없애고 자신이 생성됨을 콘솔에 출력하게 하면 되겠죠.

 

class Campus

{

   ... 중략 ...

    internal Campus()

    {

        Console.WriteLine("캠퍼스 생성");

    }

}

 

 실행하면 다음과 초기화 부분에서는 다음과 같은 내용이 화면에 출력될 것입니다.

 

▶ 실행 결과

캠퍼스 생성

강의실 생성

기숙사 생성

도서관 생성

  

 각 장소가 생성되고 나면 생성할 최대 학생 수를 입력받아 학생들을 생성하기로 되어 있습니다. 여기에서 결정할 최대 학생 수는 프로그램 전체에 영향을 끼치므로 GameRule 클래스를 추가하여 공통으로 사용할 멤버를 추가할게요. 그리고 GameRule은 개체라기보다는 공통으로 관리할 데이터들의 집합체 역할을 할 것이므로 정적 클래스로 정의하겠습니다.

 

 정적 클래스 GameRule을 프로젝트에 추가할게요. 그리고 관리할 최소 학생 수와 최대 학생 수를 const 멤버로 정의하고 사용자에 의해 결정될 최대 학생 수를 관리하는 멤버 필드를 추가하고 이에 대해 얻거나 설정할 수 있는 멤버 속성을 추가할게요. 특히, 설정하는 set 블록은 접근 한정을 private으로 지정하여 외부에서 변경할 수 없게 하겠습니다. 이럴 때 정적 생성자를 사용하면 효과적입니다.

 

static class GameRule //정적 클래스

{

    internal const int min_students = 5;

    internal const int max_students = 100;

    static int user_max_students;

    internal static int MaxStudents

    {

        get{   return user_max_students;    }

        private set

        {

            if (value < min_students){    value = min_students;    }

            if (value > max_students){    value = max_students;   }

            user_max_students = value;

        }

    }

    static GameRule()

    {

        Console.WriteLine("최대 학생 수를 입력하세요. " );

        Console.WriteLine("최소:{0} 최대:{1}", min_students, max_students);

        GameRule.MaxStudents = EHLib.GetNum();

        Console.WriteLine("최대 학생수:{0} ", GameRule.MaxStudents);

    }

}


 그리고 수를 입력하는 기능을 비롯하여 일반적으로 사용할 만한 라이브러리 형태의 기능들은 EHLib 클래스를 추가하여 제공하려고 합니다. EHLib도 정적 클래스로 제공할게요.

 

static class EHLib

{

    static internal int GetNum()

    {

        int num = 0;

        if (int.TryParse(Console.ReadLine(), out num))

        {

            return num;

        }

        return 0;

    }

}

 

 GameRule EHLib 클래스가 추가되었으므로 클래스 다이어그램을 수정하시고 학생 생성에 관한 시퀀스 다이어그램도 수정해야 할 것입니다. 이제 MakeStudents 메서드를 구현해 봅시다. 먼저, GameRule MaxStudents 속성을 통해 최대 관리할 학생 수를 얻어와서 해당 수만큼의 학생을 생성하여 캠퍼스 개체에게 보내주면 되겠죠. 물론, 각 학생을 생성하기 위해서는 어떠한 유형의 학생을 선택할 것인지와 이름을 입력받고 생성할 때 학생의 일련번호와 이름을 인자로 전달해야 할 것입니다.

 

enum StuType{ ST_CSTUDENT, ST_MSTUDENT, ST_PSTUDENT, MAX_STUTYPE }

class CampusLife

{

    ... 중략 ...

    private void MakeStudents()

    {

        int max_students = GameRule.MaxStudents;

        Student student = null;

        for (int i = 0; i < students.Length; i++)

        {

            student = MakeStudent(i + 1);

            campus.InStudent(student);

        }

    }

    private Student MakeStudent(int nth)

    {

        Console.WriteLine("{0} 번째 생성할 학생 정보를 입력하세요.", nth);

        StuType stype = SelectStuType();

        Console.WriteLine("학생 이름을 입력하세요.");

        string name = Console.ReadLine();

 

        switch (stype)

        {

            case StuType.ST_CSTUDENT: return new CStudent(nth, name);

            case StuType.ST_MSTUDENT: return new MStudent(nth, name);

            case StuType.ST_PSTUDENT: return new PStudent(nth, name);

        }

        return null;

    }

 

    private StuType SelectStuType()

    {  

        Console.WriteLine("생성할 학생 유형을 선택하세요. ");

        Console.WriteLine("{0}:도전적 (디폴트), {1}:보수적, {2}:수동적",

               (int)StuType.ST_CSTUDENT, (int)StuType.ST_MSTUDENT,

               (int)StuType.ST_PSTUDENT);

 

        switch (EHLib.GetNum())

        {

            case 0: return StuType.ST_CSTUDENT;

            case 1: return StuType.ST_MSTUDENT;

            case 2: return StuType.ST_PSTUDENT;

            default: return StuType.ST_CSTUDENT;

        }

    }

}

  

 그리고 Student 클래스의 생성자를 구현해야 할 것입니다. 입력 인자로 전달받은 이름과 번호는 생성 시에 결정되고 필터링 과정은 필요가 없으니 매핑되는 멤버 필드 없이 속성으로 캡슐화할게요. 대신 set 블록은 private으로 접근을 지정하겠습니다.

 

class Student

{

    internal string Name

    {

        get;

        private set;

    }

    internal int Num

    {

        get;

        private set;

    }

    internal Student(int snum, string name)

    {

        Num = snum;

        Name = name;

    }

    ... 중략 ...

}

 

 그리고 CStudent, MStudent, PStudent의 생성자에서는 특별히 구현할 코드가 없으므로 비어있는 상태로 변경합시다.

 

class CStudent:Student

{

    internal CStudent(int snum, string name)

        : base(snum, name)

    {

    }

    ... 중략 ...

}

 

 Campus 클래스에 InStudent 메서드를 구현하기 전에 먼저 생성자에서 학생들을 보관하기 위한 멤버를 추가합시다. 그리고 Campus 생성자에서 GameRule MaxStudents 멤버를 얻어와 배열을 생성합시다. 이와 같은 작업을 하였으면 InStudent에서는 빈 자를 찾아 해당 위치에 학생을 보관하면 되겠죠. 이처럼 작성하면 초기화 과정에 최대 학생 수를 결정하는 부분이 캠퍼스 생성과정에서 수행되므로 시나리오를 변경하고 시퀀스 다이어그램도 변경하기로 할게요.

 

class Campus

{

    Student[] students = null;

    ... 중략 ...

    internal Campus()

    {

        Console.WriteLine("캠퍼스 생성");

        students = new Student[GameRule.MaxStudents];

    }

    internal void InStudent(Student student)

    {

        int empty_index = FindEmptyIndex();

        students[empty_index] = student;

    }

    private int FindEmptyIndex()

    {

        int i;

        for (i = 0; i < students.Length; i++)

        {

            if (students[i] == null)

            {

                 break;

            }

        }

        return i;

    }

}

 

너와 나의 연결고리 "공감"

반응형