105. 도서 관리 프로그램 – 장르 추가

이제 유즈케이스별로 각 기능을 어떠한 흐름으로 수행해야 하는지 생각해 보기로 해요.
여기서는 최종 사용자, App, Genre, Book사이에 주고 받을 흐름만 정의할게요.

먼저 장르 추가 기능의 시퀀스를 정의해 보세요.

장르 추가 기능에서는 Program에서 최종 사용자에게 추가할 장르 이름을 입력할 것을 요청하게 하세요.
최종 사용자가 장르 이름을 입력하면 Program에서는 같은 이름의 장르가 있는지 확인하겠죠.
그리고 없을 때 장르를 생성하게 하세요.

App에서는 Genre에서 제공하는 GetName 기능과 장르를 생성하는 NewGenre 기능을 사용하고 있어요.
따라서 이 두 함수는 Genre에서 제공하세요.
New 함수를 제외한 나머지 함수 이름은 형식명으로 시작하고 첫 번째 인자는 형식 포인터 변수예요.
이 둘은 시퀀스 다이어그램에서는 생략하기로 해요.
Genre.h에는 App에서 사용하는 함수 선언문을 추가하세요.

동적으로 장르를 생성하는 함수가 있으니 동적으로 소멸하는 함수를 선언하세요.

Genre *NewGenre(int gnum,const char *gname);
void DeleteGenre(Genre *genre);
const char *GenreGetName(Genre *genre);

Genre.c에서 이들 함수를 구현해야겠죠.
NewGenre에서는 Genre형식 크기의 메모리를 할당한 후에 초기화 작업을 수행하고 할당한 메모리를 반환하세요.

void GenreGenre(Genre *genre,int gnum,const char *gname);
Genre *NewGenre(int gnum,const char *gname)
{
    Genre *genre = 0;
    genre = (Genre *)malloc(sizeof(Genre));
    GenreGenre(genre,gnum,gname);
    return genre;
}

초기화 작업에서는 장르 번호와 장르 이름을 입력 인자로 전달받은 값으로 설정하세요.
도서를 보관하기 위한 동적 배열을 생성하고 장르 내 도서 번호를 부여하기 위한 최근에 부여한 도서 번호를 0으로 설정하세요.

void GenreGenre(Genre *genre,int gnum,const char *gname)
{
    genre->gnum = gnum;
    memset(genre->name,0,sizeof(genre->name));
    strncpy(genre->name,gname,MAX_GNAME_LEN);
    genre->books = NewArray(0,0);
    genre->last_bnum = 0;
}

Genre 개체를 소멸하는 함수에서는 해제화 작업 후 자신의 메모리를 해제해야겠죠.

void DeleteGenre(Genre *genre)
{
    GenreTGenre(genre);
    free(genre);
}

해제화 작업에서는 도서를 보관하기 위해 동적으로 생성한 배열 개체를 소멸하세요.

void GenreTGenre(Genre *genre)
{
    DeleteEHArray(genre->books);
}

장르 이름 가져오기 함수에서는 자신의 장르 이름을 반환하세요.

const char *GenreGetName(Genre *genre)
{
    return genre->name;
}

장르 추가 기능에 앞서 App 초기화 함수에서 장르를 보관하기 위한 배열을 동적으로 생성하는 부분을 추가하세요.
해제화 함수에서 이를 소멸하는 부분을 추가하세요.

void AppApp(App *app,const char *fname)
{
    memset(app->fname,0,sizeof(app->fname));
    strncpy(app->fname,fname,FILENAME_MAX);
    app->genres = NewEHArray(0,0);
    app->last_gnum = 0;
    AppLoad(app);
    printf("아무 키나 누르세요.\n");
    getkey();
}
void AppTApp(App *app)
{
    DeleteEHArray(app->genres);
}

장르 추가 기능에서는 장르명을 입력 받게 하세요.
그리고 App에 보관한 장르에서 같은 이름의 장르가 있는지 찾아요.
만약에 없다면 장르 개체를 생성하여 배열에 보관하세요.

void AppAddGenre(App *app)
{
    char gname[MAX_GNAME_LEN+1]="";
    Iterator seek = 0;
    Genre *genre = 0;

    printf("%d번째 추가할 장르명을 입력하세요.\n",app->last_gnum+1);
    gets_s(gname,MAX_GNAME_LEN);

    seek = AppFindGenre(app,gname);
    if(seek != EHArrayEnd(app->genres))
    {
        printf("이미 존재하는 장르입니다.\n");
        return;
    }
    genre = NewGenre(app->last_gnum+1,gname);
    app->last_gnum++;
    EHArrayPushBack(app->genres,genre);
}

특정 이름의 장르를 보관한 위치를 찾는 함수를 구현하기로 해요.
배열에서는 자료를 보관한 시작 위치와 끝 위치를 함수로 제공하고 있어요.
끝 위치란 마지막 자료를 보관한 위치가 아니라 이번에 자료를 보관할 위치임을 주의하세요.
즉 마지막 자료를 보관한 다음 위치예요.
그리고 Iterator 형식 변수에 간접 연산을 취하면 보관한 요소를 얻을 수 있어요.
여러분은 자신이 보관한 형식으로 명시적 형변환하세요.

Iterator AppFindGenre(App *app,const char *gname)
{
    Iterator seek;
    Iterator end;
    Genre *sgenre=0;
    const char *sgname=0;

    seek = EHArrayBegin(app->genres);
    end = EHArrayEnd(app->genres);

    for(  ;seek != end; ++seek)
    {
        sgenre = (Genre *)(*seek);
        sgname = GenreGetName(sgenre);
        if(strcmp(sgname,gname)==0)
        {
            break;
        }
    }
    return seek;
}