티스토리 뷰

1. Observer pattern(옵저버 패턴)

  • 한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들한테 연락이 가고 자동으로 내용이 갱신되는 방식으로 일대다(one-to-many) 의존성을 정의한다.
  • 보통 옵저버 패턴은 대부분 상태를 저장하고 있는 주제 인터페이스를 구현한 하나의 주제객체와 주제객체에 의존하고 있는 옵저버 인터페이스를 구현한 여러개의 옵저버 객체가 있는 디자인을 바탕으로 한다.

가. 데이터 전달 방식

  • 주제객체에서 옵저버러 데이터를 보내는 방식(푸시 방식)
  • 옵저버에서 주제객체의 데이터를 가져가는 방식(풀 방식)

2. 디자인 원칙

  • 옵저버 패턴은 주제와 옵저버가 느슨하게 결합 되어 있는 객체 디자인을 제공
  • 주제가 옵저버에 대해서 아는 것은 옵저버가 특정 인터페이스(Observer 인터페이스)를 구현 한다는 것 뿐, 옵저버는 언제든지 새로 추가 할 수 있음(주제는 Observer 인터페이스 구현하는 객체 목록에만 의존하기 때문), 새로운 형식의 옵저버를 추가하려해도 주제를 전혀 변경할 필요 없음(새로운 클래스에서 Observer 인터페이스만 구현해주면됨), 주제나 옵저버가 바뀌더라도 서로에게 전혀 영향을 주지 않기 때문에 주제와 옵저버는 독립적으로 사용 할 수 있다.
  • 느슨하게 결합하는 디잔인을 사용하면 변경 사항이 생겨도 무난히 처리 할 수 있는 유연한 객체지향 시스템을 구축 할 수 있다.(객체 사이의 상호 의존성을 최소화 할 수 있기 때문)

3. 쉽게 이해하는 Observer pattern

  • Button과 onClickListener로 이해하기
    • 과연 우리는 Button만으로 Button을 잘 안다고 할 수 있을까? Button이 Click되었는지 사용자 즉, 클라이언트는 어떻게 알 수 있을까?
    • Button을 클릭하게 되면 상태 변화를 위해 onClickListener을 사용하는데 onClickListener도 알고 있어야 진정으로 Button을 알고 있다고 할 수 있다. 
    • 자바 GUI에서 Button을 클릭하면 이벤트가 발생하도록 OnClickListener로 동작하도록 하는데 이 메소가 Button이 클릭되었기 때문에 이벤트로 사용자,클라이언트에게 알려주게 되는것이다.
  • Observer pattern 의 핵심은 상태변화다.
    • 위에서 말했듯이 Button을 클릭 했을 때 일어나는 상태변화 동작을 하게 해 주는 것이 onClickListener다.
    • 쉽게 말해서 상태변화를 사용하는 사용자, 클라이언트에게 알려주는 것이 Observer pattern이라고 생각해라.

4. Observer pattern을 이용한 간단한 예제

가. OnRecieveListener.java <interface> // 설계 부분

package OberverStudy;

// 설계
public interface OnRecieveListener {
	void onRecieve(String message);
}

나. IClientChat.java <interface> // 설계 부분

package OberverStudy;

// 설계
public interface IClientChat {
	void sendMessage(String message);
	void addOnRecieveListener(OnRecieveListener onRecieveListener);
}

다. ChatClient.java <client> // 클라이언트 부분

package OberverStudy;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

// 클라이언트
public class ChatClient extends Thread implements IClientChat {
	int port = 64000;
	String ip = "127.0.0.1";
	private Socket socket;
	private PrintWriter out;
	private BufferedReader in;
	private String inputMessage;
	private OnRecieveListener onRecieveListener;

	public ChatClient() {
		requestToConnect();
	}

	private void requestToConnect() {
		try {
			socket = new Socket(ip, port);
			OutputStream out = socket.getOutputStream();
			InputStream in = socket.getInputStream();
			this.out = new PrintWriter(out);
			this.in = new BufferedReader(new InputStreamReader(in));
		} catch (Exception e) {
			e.printStackTrace();
		}
		this.start();
	}

	@Override
	public void run() {
		try {
			while ((inputMessage = in.readLine()) != null) {
				if (onRecieveListener != null) {
					onRecieveListener.onRecieve(inputMessage);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void sendMessage(String message) {
		out.println(message);
		out.flush();
	}

	@Override
	public void addOnRecieveListener(OnRecieveListener onRecieveListener) {
		this.onRecieveListener = onRecieveListener;
	}
}

라. ChatClientGUI <GUI>

package OberverStudy;

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;

//GUI
public class ChatClientGUI extends JFrame {
	   private JLabel label;
	   private JTextField textField;
	   private JButton button;
	   private IClientChat clientChat;
	   
	   public ChatClientGUI(IClientChat clientChat) {
	      this.clientChat = clientChat;
	      
	      label = new JLabel("");
	      textField = new JTextField("ddddd");
	      button = new JButton("전송");
	      
	      setLayout(new FlowLayout());
	      add(label);
	      add(textField);
	      add(button);
	      
	      button.addActionListener(new ActionListener() {
	         @Override
	         public void actionPerformed(ActionEvent e) {
	            clientChat.sendMessage(textField.getText());
	         }
	      });
	      
	      clientChat.addOnRecieveListener(new OnRecieveListener() {
	         @Override
	         public void onRecieve(String message) {
	            label.setText(message);
	         }
	      });
	      
	      setSize(500, 500);
	      setVisible(true);
	   }
	}

마. ChatClientMain <Main>

package OberverStudy;

public class ChatClientMain {
	public static void main(String[] args) {
		IClientChat clientChat = new ChatClient();
		ChatClientGUI gui = new ChatClientGUI(clientChat);
	}
}

바. 실행결과

  • Client / GUI 실행화면

client 실행화면

  • Server 실행화면
    • 서버는 Java Script로 작성한 코드를 사용, 다음에 기회가 되면 다룰 예정이고 오늘은 클라이언트 프로그램에 집중 한다.

server 실행화면

 

  • JTextField에 글을 작성하고 전송 버튼을 누르면 서버에게 작성된 메시지를 보낸다.
  • JTextField에 작성된 글을 JLabel에 나타나도록 구현
...더보기

실습에서의 핵심!

  • 디자인 패턴 중에서 옵저버 패턴이 가장 중요하다고 나는 생각한다.
  • 굳이 인터페이스를 선언해서 사용하는 것보다 직접 구현하는 것이 더 낫지 않을까?

결론은 아니다가 정답이다. 만약 인터페이스를 선언하지 않고 클래스를 구현하였다면 클래스의 재사용성은 떨어질 것이고 유지보수가 힘들 수 있다. 무엇보다 인터페이스를 선언함으로 Observer pattern을 적용한다면 인터페이스로 전체적인 구조만 작성해 놓는다면 Client, Server, GUI 를 구현 함에 있어서 서로의 코드를 구애 받지 않고 분담 업무가 가능하게 된다. 꼭 위와 같은 클라이언트/서버 뿐만 아니라 개발을 진행하면 Observer 패턴만 잘 적용한다면 코드의 재상용성이 높아지고 개발의 분담이 잘 이루어 질 것이다.

 

 

2019.07.25(목)

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함