② sendMessage()

사용자 이름 입력받는 거 추가하자

swing tutorial 검색

https://docs.oracle.com/javase/tutorial/uiswing/index.html

https://docs.oracle.com/javase/tutorial/uiswing/components/index.html

Customized Dialog

https://docs.oracle.com/javase/tutorial/uiswing/components/dialog.html

cancel 버튼 누르면 null이 리턴된다.
아무것도 안 입력하고 OK 누르면 빈 문자열 리턴

대화명을 입력 안 하면 안 되게끔

2자 이상이어야 합니다
nickname.length() > 1
nickname.length() >= 2
가독성 측면에서는 아래거 나음

기능별로 메서드를 묶어서 분리시킨다

sendMessage(String.format("[%s] %s", nickname, message));
sendMessage("[" + nickname + "]" + message);

super 자동 삽입 됨

this.setTitle("채팅 - " + nickname);

this 생략하기

코드 자동 생성하면 this 붙음

연결이 완료되면 연결 버튼을 종료 버튼으로 바꾸자

connectBtn 인스턴스 변수로 안 함

HTML 모든 태그에 id 부여하지 말고 찾을 일이 있을 때 부여하기

JButton connectBtn = new JButton("연결");

어차피 컴파일 하면 분리돼서
생성자의 첫 번째 줄로 들어가고

JButton connectBtn;

public ChatClient() {
  connectBtn = new JButton("연결");
  ...  
}

종료하게 되면 종료 버튼을 연결 버튼으로 바꾸고 대화 내용을 지우자

Goodbye! 보내지 말고 그냥 끝내기

스레드가 2개 떠있을 거임

일반적으로 메시지로 입력하지 않을 특별한 코드 추가

out.writeUTF("<![QUIT[]]>"); // 연결을 끊겠다는 특별한 메시지를 클라이언트에게 보낸다.

목록에 들어 있으니까

연결이 끊어지면 client 출력 도구 목록에서 제거해야 된다

clientOutputStreams.remove(out); // 메시지 출력 목록에서 연결이 종료된 클라이언트를 제거한다.

close 버튼 눌렀을 때도 해야 됨

닫혀 있는 게 계속 들어 있는 상태였음

서버는 24시간 365일 계속 실행되고 있기 때문에 자원 관리가 중요

정확하게 clean 제거를 해줘야 된다

닫기 전에 정식으로 서버에게 \\quit

예기치 않게 클라이언트 연결이 끊기면
대표적인 예로 정전
그래도 서버에는 clientOutputStreams에는 out 객체가
예외가 발생하면 더 이상 클라이언트는 유효하지 않다고 보고 제거
clientOutputStreams에는 출력 객체가 남아 있을 거

IndexOutOfBounds 에러 남

deleteStreams 삭제할 명단에 올려놔

deleteStreams.add(out); // 무효한 출력 스트림은 삭제 명단에 등록한다.

코드를 무미건조하게 읽으면 안 된다.
의미를 정확하게 설명한다.
무효한 출력 스트림은 삭제 명단에 등록한다.
아 삭제 명단에 등록해놨다가 나중에 삭제하려나 보다
그 다음 행위가 떠오름

삭제 명단에 등록된 출력 스트림을 클라이언트 목록에서 제거한다.

EC2 서버 들어갈 때마다 sudo yum update 항상 해주기

중첩 클래스 들어가야 됨

com.eomcs.oop.ex11.overview

중첩 클래스의 필요성

RuntimeException
처리하긴 해야 되지만 중간에 호출할 때마다 꼬박꼬박 try - catch를 적을 필요는 없다는 거

최소 main()에서는 해줘야 됨

JVM까지 가면 무조건 종료해버림

메서드 이름이랑 필드 이름이랑 같아도 상관 없음

삭제하려는 게 무효한 인덱스면 던져버려

찾았으면 땡긴다

맨 끝에 있는 거 지우면 그 다음거 어떻게 땡김?

맨 마지막까지 돌 필요 없음

size - 1

size 바로 전까지만 하면 됨

보통 이거 손코딩 하라고 한다.

null 할 필요 없음 --size 하니까

가비지가 안 됨
참조하는 주소가 없어야 가비지가 되는데
항상 맨 마지막은

배열의 크기를 줄이고, 마지막 항목에 들어 있는 값을 null로 초기화하여 객체의 레퍼런스를 줄인다.

참조하는 레퍼런스가 없도록 관리해야 한다

arr[--size] = null; // 배열의 크기를 줄이고, 마지막 항목에 들어 있는 값을 null로 초기화하여 객체의 레퍼런스를 줄인다.

400번지 주소를 갖고 있는 참조 변수가 사라지니까
700번지 주소를 가리키는 변수가 2개 존재하게 된다
레퍼런스 카운트 하나 증가
가비지로 다루기 힘들어진다.

size는 줄었지만 배열이기 때문에 계속 존재하는 상태고 값은 들어 있는 상태

EmptyStackException
더 이상 꺼낼 게 없으면 에러를 띄운다

거꾸로 출력됨

자료구조 혼자서 만들 수 있을 정도로 트레이닝 하기

스택에서 데이터를 꺼내는 방법

offer
poll

큐가 비어 있으면 null을 리턴한다

step1의 문제점

collection 객체의 데이터 조회 - 1단계

06-네트워킹_스레딩 / 36 페이지

MyList ← get()

MyStack ← pop()

MyQueue ← poll()

collection 객체에 따라 데이터 조회 방법이 다르다
즉, 데이터 조회 방법에 일관성이 없다.
프로그램의 유지보수가 불편하다.

collection의 종류에 상관없이 일관된 방법으로 꺼내는 방법이 없을까

collection 객체의 데이터 조회 - 2단계

06-네트워킹_스레딩 / 38 페이지

collection 유형에 상관 없이 데이터 조회하기
⟹ 데이터 조회 기능을 별도의 객체로 분리

데이터 조회 객체를 중간에 둔다
next() 하나 줘

MyList ← get() ← ListIterator ← next()

MyStack ← pop() ← StackIterator ← next()

MyQueue ← poll() ← QueueIterator ← next()

⟹ 데이터를 조회할 때 일관된 방법을 사용

호출 규칙을 정하자
인터페이스를 정의하자
이 규칙에 따라 동작하게 한다.

GoF의 Iterator 설계 패턴

데이터를 조회하는 코드를 별도의 클래스로 분리
컬렉션의 타입에 상관없이 일관성 있는 데이터 조회를 지원하는 설계 기법

<<interface>> Iterator
ListIterator List에서 데이터를 꺼내는 일을 한다.
Test1

MyList를 ListIterator에 등록한다
ListIterator에 MyList를 알려줘야 된다
MyList이 collection 객체

마우스 커서
키보드 커서

한 줄이어도 중괄호 치라고 권고

Test2
MyStack에서 직접 꺼내는 대신
StackIterator가 대신 한다
StackIterator ·············▷ Iterator 인터페이스 구현체

size()를 통해서 꺼낼 데이터가 있는지 물어보고

QueueIterator

꺼내는 방법은 List에서 꺼내나 Stack에서 꺼내나 똑같음

    StackIterator iterator = new StackIterator(myStack);
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }

이게 바로 Iterater의 존재 이유
List든 Stack이든 Queue든 일관된 방법으로 꺼낼 수 있다

collection 객체의 데이터 조회 - 3단계 : Low Coupling

Iterator 구현체의 생성을 컬렉션 객체에 맡긴다.

MyList 에 iterator() 메서드 정의하기
iterator() : Iterator
MyStack, MyQueue 에서 iterator() 메서드 오버라이딩 하기

① 이전 방식
Test1 → MyList 생성, ListIterator 생성

② 개선 방식
Test1 → MyList 생성
MyList → ListIterator 생성

    Iterator iterator = myList.iterator();
    while (iterator.hasNext()) {
      System.out.println(iterator.next());
    }

Iterator를 사용하는 Test1은 Iterator 구현 클래스를 알 필요가 없다. coupling

GRASP(General Responsibility Assignment Software Patterns)
Low Coupling
클래스 간의 종속을 줄임으로써 변경에 영향을 최소화시킨다.

변경 사항이 발생하면 사용하는 쪽에도 영향을 끼친다
사용하는 쪽이랑 사용되는 쪽이랑 둘 다 내가 만든 거면 상관없는데
실무에서는 만든 사람 따로 사용하는 사람 따로임
그게 coupling
클래스 간에 종속되어 있기 때문에 수정하면 수정해야됨

MyList가 ListIterator를 만들고 Test1이 만드는 게 아님
Test1은 ListIterator을 모름
커플링을 이렇게 줄이는 거

오버라이딩
상속 받은 메서드 중에서 서브 클래스 역할에 맞게끔 재정의 해주는 것

이것이 로우 커플링~~~

클래스 구조도는 바뀐 게 없음

collection 객체의 데이터 조회 - 4단계 : static nested class

06-네트워킹_스레딩 / 43 페이지

MyList 클래스 안에 ListIterator 클래스 static으로 집어넣기
MyStack 클래스 안에 StackIterator 클래스 static으로 집어넣기
MyQueue 클래스 안에 QueueIterator 클래스 static으로 집어넣기

ListIterator, StackIterator, QueueIterator 파일 삭제하기

🔹 스태틱 중첩 클래스 (static nested class)
이 클래스를 소유하고 있는 클래스 뿐만 아니라 다른 클래스도 사용할 수 있다.

⭐️ 특정 클래스에서만 사용되는 클래스라면 사용 범위를 제한하라!
⟹ 쓸데없이 사용 범위를 넓히는 것보다 적절한 범위로 제한하는 것이 유지보수에 더 낫다.

Test1 → MyList 생성
MyList → ListIterator 생성
Test1 → Iterator hasNext(), next() 메서드 사용

Test1은 ListIterator를 알 필요가 없다.

Test1 --사용--> MyList --생성--> <<iterator>> ListIterator

사용범위를 제한하는 문법이 nested class

static으로 바꾼다

collection 객체의 데이터 조회 - 5단계 : non-static nested class (inner class)

06-네트워킹_스레딩 / 45 페이지

MyList 클래스로 옮긴 ListIterator 클래스에서 static 빼주기
MyStack 클래스로 옮긴 StackIterator 클래스에서 static 빼주기
MyQueue 클래스로 옮긴 QueueIterator 클래스에서 static 빼주기

바깥 클래스의 객체 주소를 자동 주입하는 문법
중첩 클래스에서 바깥 클래스의 인스턴스를 사용해야 하는 상황에 적용하면 된다.

🔹 논-스태틱 중첩 클래스 (non-static nested class = inner class)
특정 인스턴스에 종속된 클래스인 경우 논-스태틱 중첩 클래스로 정의한다.
클래스 바로 밑에 선언된, 다른 메서드가 서로 공유할 수 있는, 인스턴스끼리 공유할 수 있는 인스턴스 중첩 클래스
외부 클래스의 인스턴스 멤버처럼 다루어진다.
주로 외부 클래스의 인스턴스 멤버들과 관련된 작업에 사용될 목적으로 선언된다.
인스턴스 클래스는 외부 클래스의 인스턴스 멤버를 객체 생성 없이 바로 사용할 수 있다.

public class MyList {

  ...
  
  // non-static nested class(논스태틱 중첩 클래스)
  // - inner class 라고도 부른다.
  // - ListIterator가 사용할 바깥 클래스 MyList의 인스턴스를 주소를 저장할 필드와 생성자가 자동으로 추가된다.
  // - 기존의 static nested class 처럼 개발자가 직접 추가할 필요가 없다.
  // - 바깥 클래스의 인스턴스를 사용하는 경우, static nested class로 만드는 것보다 더 편리하다.
  //
  class ListIterator implements Iterator {
  
    ...
  
  }

}

ListIterator가 사용할 바깥 클래스 MyList의 인스턴스를 주소를 저장할 필드와 생성자가 자동으로 추가된다.

Iterator ListIterator처럼

static을 떼야 한다

알아서 this가 들어간다

기본 생성자 없음
생성자가 없으면 기본 생성자가 만들어지는 거 아닌가

생성자가...

MyList.this.size()
MyList.this.get(cursor++)
현재 클래스에 동일한 이름의 메서드가 없으면 바깥클래스.this. 생략 가능. 컴파일러가 자동으로 붙임.
그냥 this 라고 하면 ListIterator 인스턴스가 됨
바깥클래스의 인스턴스 주소니까 MyList.this. 해줘야 됨

컴파일 된 클래스 파일 같이 보면서 공부하기

개발자의 코딩을 줄여준다

ListIterator 처럼 바깥 클래스의 인스턴스를 반드시 사용해야 하는 상황이라면

코딩을 줄이기 위해서는 non-static nested class 을 써라

바깥 클래스 객체 주소를 받으

collection 객체의 데이터 조회 - 6단계 : local class

collection 객체의 데이터 조회 - 7단계 : anonymous class

new [수퍼클래스명 또는 인터페이스명] () {
  ...
}

호출할

new Iterator() // Object의 기본 생성자 호출

Iterator 구현체가 리턴된다

  @Override
  public Iterator iterator() {
    // local class(로컬 클래스)
    // - 메서드나 특정 블록 안에서만 사용될 클래스라면 
    //   그 메서드나 블록에서 클래스를 정의함으로써 
    //   명시적으로 사용 범위를 더 제한할 수 있다.
    // - 단지 사용 범위를 더 제한한 것에 불과하다.
    // - 로컬 클래스에도 바깥 클래스의 인스턴스 주소를 저장할 필드와 생성자가 자동으로 추가된다.
    //
    Iterator obj = new Iterator() {

      @Override
      public boolean hasNext() {
        return MyQueue.this.size() > 0;
      }

      @Override
      public Object next() {
        return MyQueue.this.poll();
      }
    };

    return obj;
  }
  @Override
  public Iterator iterator() {
    // local class(로컬 클래스)
    // - 메서드나 특정 블록 안에서만 사용될 클래스라면 
    //   그 메서드나 블록에서 클래스를 정의함으로써 
    //   명시적으로 사용 범위를 더 제한할 수 있다.
    // - 단지 사용 범위를 더 제한한 것에 불과하다.
    // - 로컬 클래스에도 바깥 클래스의 인스턴스 주소를 저장할 필드와 생성자가 자동으로 추가된다.
    //
    return new Iterator() {

      @Override
      public boolean hasNext() {
        return MyStack.this.size() > 0;
      }

      @Override
      public Object next() {
        return MyStack.this.pop();
      }
    };

  }

Iterator를 구현한 객체 주소라 리턴되니까

람다 문법은 메서드 1개 짜리 인터페이스만 가능해서 불가능

내일 람다 문법 배운다

중첩 클래스 : 클래스의 사용 범위를 제한하는 문법

중첩 클래스의 사명...

0개의 댓글