2022.07.22 금요일/자바 내용 정리/CRUD 구현/패키지를 이용하여 클래스를 분류하는 방법 + 접근 제어 조정/공통 코드(필드,메서드)를 공유하는 방법 : 상속

Jimin·2022년 7월 22일
0

비트캠프

목록 보기
7/60

아침 알고리즘

// [문제] 
// 양의 정수 x를 2진수로 표현했을 때 1 값을 갖는 비트 개수를 정수의 무게라고 정의할 때,
// 같은 무게를 가지는 양의 정수 중에서 x와 가장 가까운 양의 정수를 구하시오!
// 예) x = 0b0000_1010 (10)
//   x와 같은 무게를 가지는 정수들
//     0b0000_1001 (9)
//     0b0000_1100 (12)
//     0b0001_0010 (18)
//     ...
//   답: 0b0000_1001 (9)
//
// [훈련 목표]
// - 2진수를 다루는 방법
// - 연산자, 흐름제어문을 다루는 방법
//
// [시간 복잡도]
// - ?
//
public class Test06 {

  public static void main(String[] args) {
    System.out.println(closestIntSameBit(0b00001010) == 0b00001001); // 10 ==> 9
    System.out.println(closestIntSameBit(0b11001000) == 0b11000100); // 200 ==> 196
  }

  static int closestIntSameBit(int x) {
    // 이 메서드를 완성하시오!
    for(int i=0;i<31;i++) {
      // 반복할 떄마다 한 비트씩 증가하면서 최하위 비트 두 개를 추출한다.
      int bit1 = (x >> i) &1; 
      int bit2 = (x >> (i+1))&1;
      if(bit1 != bit2) {// 두 비트가 서로 다르다면 맞교환한다.
        int mask = (1 << i) | (1 << (i+1)); // 맞교환할 비트를 1로 지정한다.
        x ^= mask; // 원래 값을 마스크와 XOR하여 1 로 설정된 비트의 값을 뒤집는다.
        return x;
      }
    }
    // x의 모든 비트가 0이거나 1이면 오류를 반환한다.
    throw new IllegalArgumentException("모든 비트가 0 또는 1이다.");
  }
}

board-app 프로젝트

회원관리 기능 추가: CRUD 구현

1단계 - 게시판 기능 구현을 모방하여 회원 관리 기능을 구현한다.

  • CRUD: (Create Read Update Delete), 컴퓨터 소프트웨어가 가지는 기본적인 데이터 처리 기능

  • Member 클래스 추가

  • MemberHandler 클래스 추가

  • MemberList 클래스 추가

  • App 클래스 변경

  • 원래 존재하던 Board관련 클래스 소스 파일들의 변수명만 조금씩 바꿔주었다.

  • Member 클래스 소스 코드

package com.bitcamp.board.domain;

public class Member {
  public int no;
  public String name;
  public String email;
  public String password;
  public long createdDate;
}
  • MemberHandler 클래스 소스 코드
/*
 * 회원 메뉴 처리 클래스
 */
package com.bitcamp.board.handler;

import java.util.Date;
import com.bitcamp.board.dao.MemberList;
import com.bitcamp.board.domain.Member;
import com.bitcamp.util.Prompt;

public class MemberHandler {

  private MemberList memberList = new MemberList();

  public void execute() {
    while (true) {
      System.out.println("회원:");
      System.out.println("  1: 목록");
      System.out.println("  2: 상세보기");
      System.out.println("  3: 등록");
      System.out.println("  4: 삭제");
      System.out.println("  5: 변경");
      System.out.println();

      int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");
      displayHeadline();

      switch (menuNo) {
        case 0: return;
        case 1: this.onList(); break;
        case 2: this.onDetail(); break;
        case 3: this.onInput(); break;
        case 4: this.onDelete(); break;
        case 5: this.onUpdate(); break;
        default: System.out.println("메뉴 번호가 옳지 않습니다!");
      }

      displayBlankLine();
    } // 게시판 while
  }

  private static void displayHeadline() {
    System.out.println("=========================================");
  }

  private static void displayBlankLine() {
    System.out.println(); 
  }

  private void onList() {
    System.out.println("[회원 목록]");
    System.out.println("번호 이름 이메일");

    Member[] list = this.memberList.toArray();

    for (Member member : list) {
      System.out.printf("%d\t%s\t%s\n",
          member.no, member.name, member.email);
    }

  }

  private void onDetail() {
    System.out.println("[회원 상세보기]");

    int memberNo = Prompt.inputInt("조회할 회원 번호? ");

    Member member = this.memberList.get(memberNo);

    if (member == null) {
      System.out.println("해당 번호의 회원이 없습니다!");
      return;
    }

    System.out.printf("번호: %d\n", member.no);
    System.out.printf("이름: %s\n", member.name);
    System.out.printf("이메일: %s\n", member.email);
    Date date = new Date(member.createdDate);
    System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);

  }

  private void onInput() {
    System.out.println("[회원 등록]");

    Member member = new Member();

    member.name = Prompt.inputString("이름? ");
    member.email = Prompt.inputString("이메일? ");
    member.password = Prompt.inputString("암호? ");
    member.createdDate = System.currentTimeMillis();

    this.memberList.add(member);

    System.out.println("회워을 등록했습니다.");
  }

  private void onDelete() {
    System.out.println("[회원 삭제]");

    int memberNo = Prompt.inputInt("삭제할 회원 번호? ");

    if (memberList.remove(memberNo)) {
      System.out.println("삭제하였습니다.");
    } else {
      System.out.println("해당 번호의 회원이 없습니다!");
    }
  }

  private void onUpdate() {
    System.out.println("[회원 변경]");

    int memberNo = Prompt.inputInt("변경할 회원 번호? ");

    Member member = this.memberList.get(memberNo);

    if (member == null) {
      System.out.println("해당 번호의 회원이 없습니다!");
      return;
    }

    String newName = Prompt.inputString("이름?(" + member.name + ") ");
    String newEmail = Prompt.inputString(String.format("이메일?(%s) ", member.email));

    String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
    if (input.equals("y")) {
      member.name = newName;
      member.email = newEmail;
      System.out.println("변경했습니다.");
    } else {
      System.out.println("변경 취소했습니다.");
    }
  }
}

  • MemberList 클래스 소스 코드
package com.bitcamp.board.dao;

import com.bitcamp.board.domain.Member;

// 회원 목록을 관리하는 역할
//
public class MemberList {

  private static final int DEFAULT_SIZE = 3;

  private int memberCount; 
  private Member[] members; 
  private int no = 0;

  public MemberList() {
    this.members = new Member[DEFAULT_SIZE];
  }

  public MemberList(int initCapacity) {
    this.members = new Member[initCapacity];
  }

  public Member[] toArray() {
    Member[] arr = new Member[this.memberCount];
    for (int i = 0; i < arr.length; i++) {
      arr[i] = this.members[i];
    }
    return arr;
  }

  public Member get(int memberNo) {
    for (int i = 0; i < this.memberCount; i++) {
      if (this.members[i].no == memberNo) {
        return this.members[i];
      }
    }
    return null;
  }

  public void add(Member member) {
    if (this.memberCount == this.members.length) {
      grow();
    }
    member.no = nextNo();
    this.members[this.memberCount++] = member;
  }

  public boolean remove(int memberNo) {
    int memberIndex = -1;
    for (int i = 0; i < this.memberCount; i++) {
      if (this.members[i].no == memberNo) {
        memberIndex = i;
        break;
      }
    }

    if (memberIndex == -1) {
      return false;
    }

    for (int i = memberIndex + 1; i < this.memberCount; i++) {
      this.members[i - 1] = this.members[i];
    }

    this.members[--this.memberCount] = null;

    return true;
  }

  private void grow() {
    int newSize = this.members.length + (this.members.length >> 1);
    Member[] newArray = new Member[newSize];
    for (int i = 0; i < this.members.length; i++) {
      newArray[i] = this.members[i];
    }
    this.members = newArray;
  }

  private int nextNo() {
    return ++no;
  }
}

패키지를 이용하여 클래스를 분류하는 방법 + 접근 제어 조정

  • 유지보수하기 좋게 클래스를 역할에 따라 분류한다.

  • 패키지 분류에 따라 멤버의 접근 범위를 조정한다.

    modifier접근 범위
    public모두 접근 가능 -> 완전 공개
    protected같은 패키지 + 자식 클래스 접근 가능
    default같은 패키지 내 접근 가능
    private같은 클래스 내 접근 가능

1단계 - 데이터 타입에 해당하는 클래스를 별도의 패키지로 분류한다.

  • 사용자 정의 데이터 타입(데이터 변수 모음) 소스 파일을 관리하는 패키지 명은 domain으로 짓는다.
  • com.bitcamp.board.domain 패키지 생성
    • 데이터 타입을 담는 패키지 명:
      domain|vo(value object)|dto(data transfer object)
    • Board, Member 클래스를 이 패키지로 옮긴다.
      • 접근 범위를 조정해야 한다.
        -> 패키지가 바뀌었으므로
      • public: 모두 공개
      • protected: 같은 패키지 + 자식 패키지
      • default: 같은 패키지
      • private: 그 멤버를 가지고 있는 클래스안에서만 접근 가능

2단계 - 데이터의 저장과 조회를 다루는 클래스를 별도의 패키지로 분류한다.

  • 데이터의 저장과 조회를 persistence라고 부른다.

    데이터의 저장과 조회(persistence) 역할을 수행하는 클래스, 객체를 관리하는 패키지명은 dao로 짓는다.

  • com.bitcamp.board.dao 패키지 생성
    • XxxList 클래스를 이 패키지로 옮긴다.
    • 접근 범위를 조정 한다.
      -> 멤버를 잘못 사용하는 경우를 예방할 수 있다!

      하나의 패키지, 하나의 클래스 내에서만 사용하는 변수, 메소드는 오히려 private으로 접근을 제한해주자!!

      • 외부에 공개(호출)할 멤버(변수, 메서드) -> public으로 설정
      • 내부에서만 사용(호출)할 멤버 -> private으로 설정

3단계 - UI를 처리하는 클래스를 별도의 패키지로 분류한다.

UI(입력과 출력) 역할을 수행하는 클래스, 객체를 관리하는 패키지명은 handler로 짓는다.

  • com.bitcamp.board.handler
    • XxxHandler 클래스를 이 패키지로 옮긴다.
    • 접근 범위를 조정한다.

4단계 - utility에 해당하는 클래스를 별도의 패키지로 분류한다.

utility는 일상생활에서 사용가능 한 것들을 추가

  • com.bitcamp.util 패키지 생성
    • Prompt 클래스를 이 패키지로 옮긴다.

공통 코드를 공유하는 방법: 상속

  • 공통 코드: 필드(변수), 메서드
  • 서로 관련된 클래스에 공통으로 나타나는 코드가 있다면, 상속을 이용하여 공유한다.
  • 상속 종류
    • 일반화(generalization):
      • 클래스들의 공통 코드(필드, 메서드)를 추출하여 별도의 클래스로 정의하는 것
      • 이렇게 정의한 클래스를 공유하는 것
    • 전문화(specialization):
      • 기존 클래스를 연결하고 필드나 변수를 추가하여 역할을 특화시키는 것
  • 다형성(polymorphism): 상황에 맞추어, 여러가지 용도로 사용 되는 것
    • 다형적 변수(polymorphism variables): 상위 타입의 변수는 하위타입의 개체를 가리킬 수 있다.

      상위 타입 = 하위 타입
      상위 타입은 하위 타입을 담을 수 있다. (Car car = new Sedan() 가능)
      상위 타입은 하위 타입을 가르킬 수 있다.
      하지만, 하위 타입은 상위 타입을 가리킬 수 없다. (Sedan car = new Car() 불가능)

  • 상속 비유; 음료만을 담을 수 있는 컵을 상위 클래스라고 할 때, 콜라, 사이다, 오렌지 주스 등등은 하위 클래스라고 할 수 있다. 이때, 컵은 음료들을 담을 수 있지만, 음료들은 컵을 담지 못한다.

    - Q. 상속 문법이란? --> 코드 공유 방법 중 하나이다!

  • 오버라이딩(Overriding)
    • 상속 받은 수퍼 클래스의 메서드서브 클래스의 역할에 맞춰 재정의하는 것
    • 상속을 받고 수퍼 클래스에 있는 메소드의 이름과 같더라도, 기능은 완전히 새롭게, 새로 내가 만들고 싶은대로 만들 수 있다. 또한 원래의 수퍼 클래스의 메서드를 이용하고 싶다면, 이용할 수도 있음
      -> super.메서드명(parameter) 이런 식으로
      -> 그냥 super. 이용 안하면 완전 새로운 함수라고 생각해도 될 것 같다.

분류와 자바 클래스

  • 자바의 모든 클래스상위, 하위 계층을 구성하고 있다.
  • 자바의 모든 클래스는 object를 최상위 클래스로 갖는다.

- Object 클래스: 자바의 최상위 클래스

  • String
  • java.util.Date
    • java.sql.Date
  • InputStream
    • FileInputStream
  • Integer
  • Member(내가 만든 데이터 타입 클래스)
  • Board(내가 만든 데이터 타입 클래스)
  • Object 클래스는 밑에와 같이 모든 클래스들을 담고 가르킬 수 있다. 심지어 내가 만든 클래스까지!
Object obj;
obj = new Object();
obj = new String();
obj = new Integer();
obj = new Member();
obj = new Boards();
  • 위의 obj 변수는 다형적 변수이다.
  • 상위 클래스 레퍼런스 변수는 하위 클래스 인스턴스를 저장할 수 있다.
    ==> 하위 클래스의 인스턴스를 가르킬 수 있다.
  • 상위 분류는 하위 분류를 가르킬 수 있다. ==> 담을 수 있다!

상위 클래스와 하위 클래스: 코드 공유를 위해!

- Q. 상속 문법이란? --> 코드 공유 방법 중 하나이다!

  • 코드 공유 방법이란? 상위 클래스의 코드자신의 코드처럼 사용할 수 있게 만드는 문법
    ==> 코드 중복을 없애는 방법


  • Super 클래스(parent 클래스):
    Object 클래스에는 모든 클래스가 기본으로 가져야 할 필드와 메서드가 정의 되어 있다.

--> String 클래스가 Object 클래스의 기능을 상속(inheritance)한다.

  • Sub 클래스(child 클래스):
    String 클래스= Object의 필드와 메서드 사용권 + 문자열을 다루는데 필요한 필드와 메서드
  • String 클래스는 object의 필드와 메서드를 가져오는 것이 아니다!! 사용권을 가져오는 것 뿐이다.
  • 자바의 모든 클래스는 Object 클래스의 자손 클래스이다!

- 클래스 정의와 super 클래스 지정하기

class Board extends Object{

}

extends Object는 생략 가능하다.

class Board{

}

상속 문법과 다형성

  • 다형성을 구현할 수 있는 세 가지 방법
    • 다형적 변수; 상속과 관련 O
    • 오버라이딩(overriding); 상속과 관련 O
    • 오버로딩(overloading)

상속과 코드 사용

  • Car class (start(), stop(), run())
    • Sedan class (openSunroof())
    • DumpTruck class (dump())
Car c;
c = new Car();
c.start();
c.stop();
c.run();

c = new Sedan();
c.start();
c.stop();
c.run();
//------------
c.openSunroof(); // 오류, 비록 실제로 c가 Sedan을 가르키고 있더라도, 메소드 사용은 c변수의 타입에 한정된다.

Sedan s;
s = new Sedan();
// super 클래스의 기능을 사용할 수 있다.
s.start();
s.stop();
s.run();
s.openSunroof();
//-----------
s = new Car(); // 오류, 모든 차를 세단이라고 할 수 없다. 세단은 차를 담을 수 없다.
// 위와 같은 사용을 미연에 방지하고자 하위레퍼런스는 상위 객체를 가르킬 수 없게 문법으로 막고 있다.

  • ObjectList 클래스를 만들어서 일반화를 진행하기 전 BoardList와 MemberList 클래스의 상태 도식화
  • ObjectList 클래스를 만들어서 일반화를 진행한 후의 상태 도식화

1단계 - BoardList와 MemberList의 공통 필드와 메서드를 찾아 분리한다.

  • ObjectList 클래스 생성
package com.bitcamp.board.dao;

public class ObjectList {
  private static final int DEFAULT_SIZE = 3;

  //Board와 Member를 둘 다 담을 수 있는 배열, 다형성

  // 서브 클래스에서 직접 접근할 수 있도록 접근 범위를 넓힌다.
  protected int length; // 게시글의 현재 개수 
  protected Object[] list; // protected: 같은 패키지 + 서브 클래스인 경우 접근 가능하다.
  //  private Board[] boards; 
  //  private Member[] members; 

  //생성자
  public ObjectList() {
    this.list = new Object[DEFAULT_SIZE];
  }

  public ObjectList(int initCapacity) {
    this.list = new Object[initCapacity];
  }

  // 목록에 저장된 인스턴스를 꺼내서 리턴한다.
  public Object[] toArray() {
    Object[] arr = new Object[this.length];
    for (int i = 0; i < arr.length; i++) {
      arr[i] = this.list[i];
    }
    return arr;
  }


  //목록에서 지정된 인덱스의 값을 찾아 리턴한다.
  // => 목록에서 값을 꺼낼 때 인덱스에 기반해서 꺼낸다.
  // => 왜? 다용도로 쓰기 위해서!!!
  //    어떤 경우에는, 데이터는 번호가 아닌 이메일이나 아이디를 가지고
  //    데이터를 꺼낼 수 있기 때문이다.
  //    어떤 값으로 찾더라도 상관없도록!!!
  public Object get(int index) {
    if(index <0 || index >= this.length) {
      return null;
    }
    return list[index];
  }

  // Board나 Member 인스턴스를 배열에 저장한다.
  public void add(Object object) {
    if (this.length == this.list.length) {
      grow();
    }
    this.list[this.length++] = object;
  }

  private void grow() {
    int newSize = this.list.length + (this.list.length >> 1);
    Object[] newArray = new Object[newSize];
    for (int i = 0; i < this.list.length; i++) {
      newArray[i] = this.list[i];
    }
    this.list = newArray;
  }



  public boolean remove(int index) {
    if(index<0 || index >= this.length) {
      return false;
    }
    for (int i = index + 1; i < this.length; i++) {
      this.list[i - 1] = this.list[i];
    }
    this.list[--this.length] = null;
    return true;
  }
}
  • 상속-> 슈퍼 클래스는 수정할일이 없고 서브 클래스만 슈퍼에서 가져와서 내가 원하는데로 커스터마이징, 수정할 일이 있기 때문에 버그가 생길일이 없다.

2단계 - ObjectList를 상속 받아 BoardList와 MemberList를 정의한다.

  • BoardList 클래스 변경

  • MemberList 클래스 변경

  • BoardHandler 클래스 변경

  • MemberHandler 클래스 변경

  • sub 클래스에서 super 클래스로 넘어가는 object는 넘어오고 난 다음, super 클래스 내부에서는 따로 (Member)object와 같이 형을 명시해줄 필요가 없다.

  • 상속을 이용할 때 this.메소드명과 super().메소드명의 차이

    • this.메소드명을 이용하면, 현재 클래스부터 해당 메소드를 찾고, 없으면 상속받은 클래스를 타고 올라가며 찾는다.
    • super().메소드명을 이용하면, 현재 클래스의 상속 클래스부터 찾고, 없으면 상속받은 클래스가 상속 받은 클래스를 타고 올라가며 찾는다.

오버라이딩의 이유: 상속 받은 클래스의 메서드를 서브 클래스의 역할에 맞춰서 사용하려고 오버라이딩을 진행한다!

  • object 타입을 이용할 때는 내가 진짜 이용하고자 하는 데이터 타입을 강제로 명시해주어야 한다.
  • 그런데 다시 super 클래스로 값을 넘겨줄 때는 상위 클래스로 넘겨줄 때,
    super 클래스의 메소드의 parameter의 데이터 타입이 object라면,
    object는 최상위 클래스이기 때문에 object포함 그 하위 클래스는 데이터타입 상관없이 모두 넘겨주는 것이 가능하다.
    ==> 그렇기 때문에 밑의 두 코드 모두 가능하다.
@Override
  public void add(Object object) {
    Board board = (Board) object;
    board.no = nextNo(); // 게시글 추가 전에 게시글의 번호 먼저 설정해주는 것을 원해서 오버라이딩
    // 수퍼 클래스의 add()를 사용하여 처리
    super.add(board);
  }
 @Override
  public void add(Object object) {
    // TODO Auto-generated method stub
    // 게시글을 그냥 바로 넣으면 안되고 번호설정하고 넣어야해!
    Member member = (Member) object; // 얘 원래 멤버인거 알려준거임
    member.no = nextNo();
    super.add(object); /// 근데 그냥 member 넣으면 안되나? 가능,
    // 원래 import한 add의 파라미터의 데이터 타입이 오브젝트라서
    // 오브젝트의 서브클래스 타입은 다 넣을 수 있다.
  }
  • super 클래스에서 object 데이터 타입 배열을 가져와서 내가 원하는 데이터 타입으로 변형해서 사용하기 위해서는, 바로 변형은 불가능하고,
    직접 사용하기 직전까지 일단 Object로 값을 받아주고, Object로 이용하다가
    직접 사용하기 직전에 배열 중 한 원소를 정해, 내가 원하는 타입값으로 강제로 변형해준다.
// boardList 인스턴스에 들어 있는 데이터 목록을 가져온다.
    Object[] list = this.boardList.toArray();
    // Object 타입인데 Board에 들어가서 오류가는 것임
    /// 하지만 강제로 형변환하면 오류 뜬다.. (Board[]를 붙임오류남 ㅠㅠ)
    // 그래서 일단 Object로 받고, 나중에 사용하기 직전
    for (Object object : list) {
      // 즉 여기서 원래의 타입 Board로 형변환을 해준다!
      Board board = (Board)object;
      Date date = new Date(board.createdDate);
      String dateStr = formatter.format(date); 
      System.out.printf("%d\t%s\t%d\t%s\t%s\n",
          board.no, board.title, board.viewCount, board.writer, dateStr);
    }

  • BoardList 클래스 소스 파일
package com.bitcamp.board.dao;

import com.bitcamp.board.domain.Board;

// 게시글 목록을 관리하는 역할
//
public class BoardList extends ObjectList {

  private int no = 0;

  // 상속 이용!
  // 수퍼 클래스의 get() 메서드는 인덱스로 항목을 찾는다
  // 그래서 Board 객체를 다루기에 적합하지 않다.
  // 따라서 다음 메서드 처럼 Board 객체를 조회하는데 적합한 메서드를 추가한다.
  // 이 메서드는 게시글 번호에 해당하는 Board 인스턴스를 찾아 리턴한다.

  // 슈퍼 클래스의 get() 메서드를 BoardList에 맞게 재정의한다.
  // -> 파라미터는 인덱스가 아닌 게시글 번호가 되게 한다.
  // --> Override이라고 부른다. (슈퍼를 서브에서 재정의)
  @Override
  public Board get(int boardNo) {
    for (int i = 0; i < this.length; i++) {
      Board board = (Board)this.list[i]; // Object배열에 실제 들어 있는 것은 Board라고 컴파일러에게 알린다.
      if (board.no == boardNo) {
        return board;
      }
    }
    return null;
  }



  // 수퍼 클래스의 add(Object)를 BoardList에 맞게끔 정의한다.
  // 같은 이름의 유사 기능을 수행하는 메서드를 추가 정의한다.

  // => 파라미터로 받은 Board 인스턴스의 no 변수값을 설정한 다음 배열에 저장한다.
  // => Overriding
  @Override
  public void add(Object object) {
    Board board = (Board) object;
    board.no = nextNo(); // 게시글 추가 전에 게시글의 번호 먼저 설정해주는 것으 원해서 오버라이딩
    // 수퍼 클래스의 add()를 사용하여 처리
    super.add(board);
  }
  //상속 받은 클래스의 메서드를 서브 클래스의 역할에 맞춰서 사용하려고
  // 오버라이딩을 진행한다! 이것이 오버라이딩을 하는 이유이다!



  // 수퍼 클래스의 remove()를 BoardList 클래스의 역할을 맞춰 재정의한다.
  @Override
  public boolean remove(int boardNo) {
    int boardIndex = -1;
    for (int i = 0; i < this.length; i++) {
      Board board = (Board) this.list[i];
      if (board.no == boardNo) {
        boardIndex = i;
        break;
      }
    }
    if (boardIndex == -1) {
      return false;
    }
    return super.remove(boardIndex); // 재정의 하기 전의 수퍼 클래스의 메서드를 호출한다.
  }


  private int nextNo() {
    return ++no;
  }
}
  • MemberList 클래스 소스 파일
package com.bitcamp.board.dao;

import com.bitcamp.board.domain.Member;

// 회원 목록을 관리하는 역할
//
public class MemberList extends ObjectList {
  // 선배가 작성한 코드 이제 내것 처럼 작성할 수 있다!
  private int no = 0;
  private int nextNo() {
    return ++no;
  }
  @Override
  public void add(Object object) {
    // TODO Auto-generated method stub
    // 게시글을 그냥 바로 넣으면 안되고 번호설정하고 넣어야해!
    Member member = (Member) object; // 얘 원래 멤버인거 알려준거임
    member.no = nextNo();
    super.add(object); /// 근데 그냥 member 넣으면 안되나? 
    // 원래 import한 add의 파라미터의 데이터 타입이 오브젝트라서
    // 오브젝트의 서브클래스 타입은 다 넣을 수 있다.
  }

  @Override
  public Member get(int memberNo) {
    // TODO Auto-generated method stub
    int idx=-1;
    for(int i=0;i<this.length;i++) {
      Member member = (Member)this.list[i];
      if(member.no ==memberNo) {
        return member;
      }
    }
    return null;
  }

  @Override
  public boolean remove(int memberNo) {
    // TODO Auto-generated method stub
    int idx=-1;
    for(int i=0;i<this.length;i++) {
      Member member = (Member)this.list[i];
      if(member.no ==memberNo) {
        idx =i;
        break;
      }
    }
    return super.remove(idx);
  }
}
  • BoardHandler 클래스 소스 파일
/*
 * 게시글 메뉴 처리 클래스
 */
package com.bitcamp.board.handler;

import java.text.SimpleDateFormat;
import java.util.Date;
import com.bitcamp.board.dao.BoardList;
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.Prompt;

public class BoardHandler {

  private String title; // 게시판의 제목

  // 게시글 목록을 관리할 객체 준비
  private BoardList boardList = new BoardList();

  public BoardHandler() {
    this.title = "게시판";
  }

  public BoardHandler(String title) {
    this.title = title;
  }

  public void execute() {
    while (true) {
      System.out.printf("%s:\n", this.title);
      System.out.println("  1: 목록");
      System.out.println("  2: 상세보기");
      System.out.println("  3: 등록");
      System.out.println("  4: 삭제");
      System.out.println("  5: 변경");
      System.out.println();

      int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");
      displayHeadline();

      // 다른 인스턴스 메서드를 호출할 때 this에 보관된 인스턴스 주소를 사용한다. 
      switch (menuNo) {
        case 0: return;
        case 1: this.onList(); break;
        case 2: this.onDetail(); break;
        case 3: this.onInput(); break;
        case 4: this.onDelete(); break;
        case 5: this.onUpdate(); break;
        default: System.out.println("메뉴 번호가 옳지 않습니다!");
      }

      displayBlankLine();
    } // 게시판 while
  }

  private static void displayHeadline() {
    System.out.println("=========================================");
  }

  private static void displayBlankLine() {
    System.out.println(); // 메뉴를 처리한 후 빈 줄 출력
  }

  private void onList() {
    SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

    System.out.printf("[%s 목록]\n", this.title);
    System.out.println("번호 제목 조회수 작성자 등록일");

    // boardList 인스턴스에 들어 있는 데이터 목록을 가져온다.
    Object[] list = this.boardList.toArray();
    // Object 타입인데 Board에 들어가서 오류가는 것임
    /// 하지만 강제로 형변환하면 오류 뜬다.. (Board[]를 붙임오류남 ㅠㅠ)
    // 그래서 일단 Object로 받고, 나중에 사용하기 직전
    for (Object object : list) {
      // 즉 여기서 원래의 타입 Board로 형변환을 해준다!
      Board board = (Board)object;
      Date date = new Date(board.createdDate);
      String dateStr = formatter.format(date); 
      System.out.printf("%d\t%s\t%d\t%s\t%s\n",
          board.no, board.title, board.viewCount, board.writer, dateStr);
    }

  }

  private void onDetail() {
    System.out.printf("[%s 상세보기]\n", this.title);

    int boardNo = Prompt.inputInt("조회할 게시글 번호? ");

    // 해당 번호의 게시글이 몇 번 배열에 들어 있는지 알아내기
    Board board = this.boardList.get(boardNo);

    // 사용자가 입력한 번호에 해당하는 게시글을 못 찾았다면
    if (board == null) {
      System.out.println("해당 번호의 게시글이 없습니다!");
      return;
    }

    System.out.printf("번호: %d\n", board.no);
    System.out.printf("제목: %s\n", board.title);
    System.out.printf("내용: %s\n", board.content);
    System.out.printf("조회수: %d\n", board.viewCount);
    System.out.printf("작성자: %s\n", board.writer);
    Date date = new Date(board.createdDate);
    System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);

  }

  private void onInput() {
    System.out.printf("[%s 등록]\n", this.title);

    Board board = new Board();

    board.title = Prompt.inputString("제목? ");
    board.content = Prompt.inputString("내용? ");
    board.writer = Prompt.inputString("작성자? ");
    board.password = Prompt.inputString("암호? ");
    board.viewCount = 0;
    board.createdDate = System.currentTimeMillis();

    this.boardList.add(board);

    System.out.println("게시글을 등록했습니다.");
  }

  private void onDelete() {
    System.out.printf("[%s 삭제]\n", this.title);

    int boardNo = Prompt.inputInt("삭제할 게시글 번호? ");

    if (boardList.remove(boardNo)) {
      System.out.println("삭제하였습니다.");
    } else {
      System.out.println("해당 번호의 게시글이 없습니다!");
    }
  }

  private void onUpdate() {
    System.out.printf("[%s 변경]\n", this.title);

    int boardNo = Prompt.inputInt("변경할 게시글 번호? ");

    Board board = this.boardList.get(boardNo);

    if (board == null) {
      System.out.println("해당 번호의 게시글이 없습니다!");
      return;
    }

    String newTitle = Prompt.inputString("제목?(" + board.title + ") ");
    String newContent = Prompt.inputString(String.format("내용?(%s) ", board.content));

    String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
    if (input.equals("y")) {
      board.title = newTitle;
      board.content = newContent;
      System.out.println("변경했습니다.");
    } else {
      System.out.println("변경 취소했습니다.");
    }
  }
}
  • MemberHandler 클래스 소스 파일
* 회원 메뉴 처리 클래스
 */
package com.bitcamp.board.handler;

import java.util.Date;
import com.bitcamp.board.dao.MemberList;
import com.bitcamp.board.domain.Member;
import com.bitcamp.util.Prompt;

public class MemberHandler {
  private String member;

  public MemberHandler(String member) {
    this.member = member;
  }
  private MemberList memberList = new MemberList();

  public void execute() {
    while (true) {
      System.out.printf("%s:\n", this.member);
      System.out.println("  1: 목록");
      System.out.println("  2: 상세보기");
      System.out.println("  3: 등록");
      System.out.println("  4: 삭제");
      System.out.println("  5: 변경");
      System.out.println();

      int menuNo = Prompt.inputInt("메뉴를 선택하세요[1..5](0: 이전) ");
      displayHeadline();

      switch (menuNo) {
        case 0: return;
        case 1: this.onList(); break;
        case 2: this.onDetail(); break;
        case 3: this.onInput(); break;
        case 4: this.onDelete(); break;
        case 5: this.onUpdate(); break;
        default: System.out.println("메뉴 번호가 옳지 않습니다!");
      }

      displayBlankLine();
    } // 게시판 while
  }

  private static void displayHeadline() {
    System.out.println("=========================================");
  }

  private static void displayBlankLine() {
    System.out.println(); 
  }

  private void onList() {
    System.out.printf("[%s 목록]\n", this.member);
    System.out.println("번호 이름 이메일");

    Object[] list = this.memberList.toArray();

    for (Object object : list) {
      Member member = (Member) object;
      System.out.printf("%d\t%s\t%s\n",
          member.no, member.name, member.email);
    }

  }

  private void onDetail() {
    System.out.printf("[%s 상세보기]\n", this.member);

    int memberNo = Prompt.inputInt("조회할 회원 번호? ");

    Member member = this.memberList.get(memberNo);

    if (member == null) {
      System.out.println("해당 번호의 회원이 없습니다!");
      return;
    }

    System.out.printf("번호: %d\n", member.no);
    System.out.printf("이름: %s\n", member.name);
    System.out.printf("이메일: %s\n", member.email);
    Date date = new Date(member.createdDate);
    System.out.printf("등록일: %tY-%1$tm-%1$td %1$tH:%1$tM\n", date);

  }

  private void onInput() {
    System.out.printf("[%s 등록]\n", this.member);

    Member member = new Member();

    member.name = Prompt.inputString("이름? ");
    member.email = Prompt.inputString("이메일? ");
    member.password = Prompt.inputString("암호? ");
    member.createdDate = System.currentTimeMillis();

    this.memberList.add(member);

    System.out.println("회원을 등록했습니다.");
  }

  private void onDelete() {
    System.out.printf("[%s 삭제]\n", this.member);

    int memberNo = Prompt.inputInt("삭제할 회원 번호? ");

    if (memberList.remove(memberNo)) {
      System.out.println("삭제하였습니다.");
    } else {
      System.out.println("해당 번호의 회원이 없습니다!");
    }
  }

  private void onUpdate() {
    System.out.printf("[%s 변경]\n", this.member);

    int memberNo = Prompt.inputInt("변경할 회원 번호? ");

    Member member = this.memberList.get(memberNo);

    if (member == null) {
      System.out.println("해당 번호의 회원이 없습니다!");
      return;
    }

    String newName = Prompt.inputString("이름?(" + member.name + ") ");
    String newEmail = Prompt.inputString(String.format("이메일?(%s) ", member.email));

    String input = Prompt.inputString("변경하시겠습니까?(y/n) ");
    if (input.equals("y")) {
      member.name = newName;
      member.email = newEmail;
      System.out.println("변경했습니다.");
    } else {
      System.out.println("변경 취소했습니다.");
    }
  }
}

profile
https://github.com/Dingadung

0개의 댓글