// 게시글 목록을 관리하는 역할
//
public class BoardDao {
LinkedList list = new LinkedList();
private int boardNo = 0;
public void insert(Object e) {
Board board = (Board) e;
board.no = nextNo();
list.append(e);
}
public Board findByNo(int boardNo) {
for (int i = 0; i < list.length(); i++) {
Board board = (Board) list.retrieve(i);
if (board.no == boardNo) {
return board;
}
}
return null;
}
public boolean delete(int boardNo) {
for (int i = 0; i < list.length(); i++) {
Board board = (Board) list.retrieve(i);
if (board.no == boardNo) {
return list.delete(i) != null;
}
}
return false;
}
public Board[] findAll() {
Object[] arr = list.getArray();
Board[] boards = new Board[arr.length];
for (int i = 0; i < arr.length; i++) {
boards[i] = (Board) arr[i];
}
return boards;
}
private int nextNo() {
return ++boardNo;
}
}
public class MemberDao {
// MemberDao 가 사용할 의존 객체를 선언한다.
ObjectList list = new ObjectList();
// ObjectList를 상속 받지 않기 때문에
// 목록에 데이터를 추가하고 싶다면
// MemberDao 클래스에 해당 메서드를 직접 정의해야 한다.
// 물론, 실제 작업은 ObjectList 가 할 것이다.
//
public void insert(Member member) {
list.add(member);
}
// MemberList 에서 MemberDao 로 바꿔는 것에 맞춰
// 메서드의 이름도 데이터에 초점을 맞춰 변경한다.
//
public Member findByEmail(String email) {
for (int i = 0; i < list.size(); i++) {
Member member = (Member) list.get(i);
if (member.email.equals(email)) {
return member;
}
}
return null;
}
public boolean delete(String email) {
for (int i = 0; i < list.size(); i++) {
Member member = (Member) list.get(i);
if (member.email.equals(email)) {
return list.remove(i);
}
}
return false;
}
public Member[] findAll() {
//목록에 저장된 회원 데이터를 가져온다.
Object arr[] = list.toArray();
//Object배열의 값을 Member배열로 옮긴다.
Member [] members = new Member[arr.length];
for(int i=0; i < arr.length; i++) {
members[i] = (Member)arr[i];
}
return members;
}
}
메서드 형식
- Method signature
- Function prototype
- 메서드명
- 파라미터 선언
- 리턴 타입
같은 인터페이스를 구현한 클래스끼리는 메서드 규격이 같기 때문에 클래스가 교체되더라도 코드 변경을 최소화 시킬 수 있다.
public interface List {
void add(Object value);
Object get(int index); // 목록에서 인덱스에 해당하는 항목을 꺼내는 메서드의 형식.
Object remove(int index); // 목록에서 인덱스에 해당하는 항목을 삭제하는 메서드의 형식.
Object[] toArray(); // 목록에 저장된 항목들을 배열에 담아 리턴하는 메서드의 형식.
int size(); // 목록에 저장된 항목의 개수를 리턴하는 메서드의 형식.
}
인터페이스 규격에 따라 메서드를 정의하는 것 또한 오버라이딩으로 간주한다.
package com.bitcamp.util;
//List 규격에 따라 메서드를 구현할 것이라고 선언한다!
// 만약 규격에 따라 메서드를 구현하지 않으면 컴파일을 안해준다!
public class ObjectList implements List {
private static final int DEFAULT_CAPACITY = 10;
private int size;
private Object[] elementData;
public ObjectList() {
elementData = new Object[DEFAULT_CAPACITY];
}
public ObjectList(int initialCapacity) {
elementData = new Object[initialCapacity];
}
@Override // 인터페이스 규격에 따라 메서드를 정의하는 것도 오버라이딩으로 간주한다.
public void add(Object e) {
if (size == elementData.length) {
grow();
}
elementData[size++] = e;
}
@Override
public Object[] toArray() {
Object[] arr = new Object[size];
for (int i = 0; i < arr.length; i++) {
arr[i] = elementData[i];
}
return arr;
}
@Override
public Object get(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스가 무효함!");
}
return elementData[index];
}
@Override
public Object remove(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스가 무효합니다!");
}
//삭제한 객체를 리턴할 수 있도록 임시 변수에 담아 둔다.
Object deleted = elementData[index];
for (int i = index + 1; i < size; i++) {
elementData[i - 1] = elementData[i];
}
elementData[--size] = null;
return deleted;
}
@Override
public int size() {
return size;
}
private void grow() {
int newCapacity = elementData.length + (elementData.length >> 1);
Object[] newArray = new Object[newCapacity];
for (int i = 0; i < elementData.length; i++) {
newArray[i] = elementData[i];
}
elementData = newArray;
}
}
package com.bitcamp.board.dao;
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.List;
import com.bitcamp.util.ObjectList;
// 게시글 목록을 관리하는 역할
//
public class BoardDao {
// List 인터페이스 레퍼런스인, list 변수는
// List 규격에 따라 만든 객체 주소를 담을 수 있다.
List list = new ObjectList();
private int boardNo = 0;
public void insert(Object e) {
Board board = (Board) e;
board.no = nextNo();
//List규격에 따라 만든 객체를 사용하여 목록에 추가한다.
// => 메서드를 호출할 때는 List 규격에 따라 호출한다.
list.add(e);
}
public Board findByNo(int boardNo) {
for (int i = 0; i < list.size(); i++) {
Board board = (Board) list.get(i);
if (board.no == boardNo) {
return board;
}
}
return null;
}
public boolean delete(int boardNo) {
// 의존 객체 ObjectList을 이용하여 목록에 저장된 게시글을 찾아 삭제한다.
for (int i = 0; i < list.size(); i++) {
Board board = (Board) list.get(i);
if (board.no == boardNo) {
return list.remove(i) != null;
}
}
return false;
}
public Board[] findAll() {
Object[] arr = list.toArray();
Board[] boards = new Board[arr.length];
for (int i = 0; i < arr.length; i++) {
boards[i] = (Board) arr[i];
}
return boards;
}
private int nextNo() {
return ++boardNo;
}
}
package com.bitcamp.util;
/**
* Node를 이용해 값을 목록을 관리하는 일을 한다.
*
* @author vivi
*
*/
// LinkedList class도 ObjectList처럼 List 규격에 따라 만든다.
// 규격이 같으면 두 객체를 서로 교체할 수 있다. ObjectLsit <-> LinkedList class
public class LinkedList implements List {
private Node head;
private Node tail;
private int size;
@Override
public void add(Object value) {
Node node = new Node(value);
size++;
if (tail == null) {
head = tail = node;
return;
}
tail.next = node;
node.prev = tail;
tail = node;
}
@Override
public Object get(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
Node cursor = head;
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
return cursor.value;
}
@Override
public Object remove(int index) {
if (index < 0 || index >= size) {
throw new ListException("인덱스의 범위를 초과했습니다!");
}
size--;
Object deleted;
if (head == tail) {
deleted = head.value;
head.value = null;
head = tail = null;
return deleted;
}
Node cursor = head;
for (int i = 0; i < index; i++) {
cursor = cursor.next;
}
if (cursor.prev != null) {
cursor.prev.next = cursor.next;
} else {
head = cursor.next;
head.prev = null;
}
if (cursor.next != null) {
cursor.next.prev = cursor.prev;
} else {
tail = cursor.prev;
tail.next = null;
}
deleted = cursor.value;
cursor.value = null;
cursor.prev = null;
cursor.next = null;
return deleted;
}
@Override
public int size() {
return size;
}
@Override
public Object[] toArray() {
Object[] arr = new Object[size];
Node cursor = head;
for (int i = 0; i < size; i++) {
arr[i] = cursor.value;
cursor = cursor.next;
}
return arr;
}
}
package com.bitcamp.board.dao;
import com.bitcamp.board.domain.Board;
import com.bitcamp.util.LinkedList;
import com.bitcamp.util.List;
// 게시글 목록을 관리하는 역할
//
public class BoardDao {
// List 인터페이스 레퍼런스인, list 변수는
// List 규격에 따라 만든 객체 주소를 담을 수 있다.
List list = new LinkedList();
private int boardNo = 0;
public void insert(Object e) {
Board board = (Board) e;
board.no = nextNo();
//List규격에 따라 만든 객체를 사용하여 목록에 추가한다.
// => 메서드를 호출할 때는 List 규격에 따라 호출한다.
list.add(e);
}
public Board findByNo(int boardNo) {
for (int i = 0; i < list.size(); i++) {
Board board = (Board) list.get(i);
if (board.no == boardNo) {
return board;
}
}
return null;
}
public boolean delete(int boardNo) {
for (int i = 0; i < list.size(); i++) {
Board board = (Board) list.get(i);
if (board.no == boardNo) {
return list.remove(i) != null;
}
}
return false;
}
public Board[] findAll() {
Object[] arr = list.toArray();
Board[] boards = new Board[arr.length];
for (int i = 0; i < arr.length; i++) {
boards[i] = (Board) arr[i];
}
return boards;
}
private int nextNo() {
return ++boardNo;
}
}
(public static final) int TIMEOUT = 10000;
public abstract void m1();
// public 을 생략할 수 있다.
abstract void m2(); // public 이 생략된 것이다. (default) 아니다!
// abstract 를 생략할 수 있다.
public void m3();
// public, abstract 모두 생략할 수 있다.
void m4();
(public) default void call(){}
(public) static void call(){}
MyInterface5Impl obj = new MyInterface5Impl(); // 인터페이스 구현체
MyInterface5Impl.m1(); // 컴파일 오류!, m1()은 static 메소드
obj.m1(); // 컴파일 오류!
private void call(){}
객체 사용 규칙을 정의한다.
그리고 클래스를 정의할 때 그 규칙에 따라 만든다.
⇒ 그러면 규칙에 따라 만든 클래스를 사용할 때는 일관된 방법으로 메서드를 호출할 수 있어 코딩하기가 훨씬 편해지고, 유지보수가 쉬워진다.
이렇게 객체의 사용 규칙(호출 규칙)을 정의하는 문법이 "인터페이스(interface)"이다.
// Worker 구현체 사용
package com.eomcs.oop.ex09.a1.before;
public class Exam01 {
public static void main(String[] args) {
BlueWorker w1 = new BlueWorker();
WhiteWorker w2 = new WhiteWorker();
JubuWorker w3 = new JubuWorker();
// 각 노동자에게 일을 시키는 방법이 다르다.
// 왜?
// => 메서드 호출 방법이 다르기 때문에
w1.doFight();
w2.doZingZing();
w3.doSsingSsing();
}
}
Worker w;
package com.eomcs.oop.ex09.a1.after;
// caller(호출자;사용자) : Exam01
// callee(피호출자;도구) : BlueWorker, JubuWorker, WhiteWorker
public interface Worker {
void execute();
}
// 구현체(implementor) - 인터페이스(사용규칙)에 따라 만든 클래스
package com.eomcs.oop.ex09.a1.after;
public class BlueWorker implements Worker {@Override
public void execute() {
System.out.println("육체 노동자가 일을 합니다!");
}
}
// Worker 구현체 사용
package com.eomcs.oop.ex09.a1.after;
public class Exam01 {
public static void main(String[] args) {
Worker w1 = new BlueWorker();
Worker w2 = new WhiteWorker();
Worker w3 = new JubuWorker();
w1.execute();
w2.execute();
w3.execute();
// 역할?
// => 사용 규칙: Worker
// => caller : Exam01
// => callee : BlueWorker, WhiteWorker, JubuWorker
}
}
default 메서드는 과거에 작성한 코드의 영향을 주지 않으면서 새로운 규칙을 추가할 수 있게 해주는 문법이다!
Myclass obj = new MyClass(); (예;obj = 200, 인스턴스의 주소값)
obj.m1();
obj.rule1();
obj.rule2();
---
ProtocolB obj2 = obj; // ProtocolB의 규칙을 따라 만들었으므로 이와 같이 선언할 수 있다.
// (obj2 = obj = 200)
obj2.rule2();
obj2.rule1();
obj2.m1(); ← 컴파일 오류, m1()은 ProtocolB에 선언된 메서드가 아니다.
// 해당 레퍼런스 변수의 데이터 타입에 존재하지 않는 메소드는 호출할 수 없다.
---
ProtocolA obj3 = obj; // ProtocolA의 규칙을 따라 만들었으므로 이와 같이 선언할 수 있다.
// (obj3 = obj = 200)
obj2.rule1();
obj2.rule2(); ← 컴파일 오류, Protocol A의 메서드가 아니다!
obj2.m1(); ← 컴파일 오류, Protocol A의 메서드가 아니다!