국비학원/수업기록

국비 지원 개발자 과정_Day16

루팽 2022. 12. 19. 19:54

UI-Event-Main 순환 관계로 설계 금지! → 인스턴스화 위치를 꼬이지 않게 잘 설정해야 한다

package dev_java;

// java.lang 패키지가 아닌 것은 모두 import 사용함
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JButton;
import javax.swing.JFrame;

class Button1 extends JFrame {
  // 선언부
  Button1Event bEvent = new Button1Event(this); // 상속, 추상클래스, 인터페이스 중심 코딩 전개 필요
  // 게으른 인스턴스화
  // 북쪽 배치
  JButton jbtn_ins = null; // 선언만 했다.(생성자 호출x)
  // 이른 인스턴스화
  // 남쪽 배치
  JButton jbtn_upd = new JButton("수정"); // 선언 및 생성(+생성자 호출까지 일어남)

  // 생성자
  Button1() {
    System.out.println("Button1 디폴트 생성자 호출" + jbtn_ins);
    // 생성자 안에서 버튼 객체 생성 -> 시점에 따라 NullPointerException 발생 가능성 생김
    initDisplay(); // new Button1()이 호출되면 자동으로 호출됨
    System.out.println("initDisplay 호출 완료==> " + (jbtn_ins == null)); // true, jbtn_ins는 null
    jbtn_ins = new JButton("입력");
    System.out.println("입력버튼 생성 완료==> " + (jbtn_ins == null)); // false, jbtn_ins는 null이 아님!
  }

  // 화면처리부
  public void initDisplay() {
    System.out.println("initDisplay 호출");
    // 동쪽 배치
    JButton jbtn_del = new JButton("삭제");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    if (jbtn_ins != null) { // null이 아니라면 jbtn_ins추가, null이면 그냥 지나침
      this.add("North", jbtn_ins);
    }
    this.add("South", jbtn_upd);
    this.add("East", jbtn_del);
    jbtn_upd.addActionListener(bEvent);
    this.setSize(400, 300);
    this.setVisible(true);
  }
}

// ActionListener의 구현체 클래스이다(이벤트 핸들러 클래스이다)
// 인터페이스는 추상메소드만 가진다.
class Button1Event implements ActionListener {
  Button1 button1 = null;
  // Button1 button1 = new Button1; // 원본이 아니라 복사본이 만들어짐!

  Button1Event(Button1 button1) {
    this.button1 = button1;
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    Object obj = e.getSource();
    // 수정 버튼을 눌렀을 때
    if (obj == button1.jbtn_upd) {
      System.out.println("수정버튼 클릭");
    }
  }
}

public class Button1Main {
  public static void main(String[] args) {
    // new를 쓰면 서로 다른 객체가 생성됨
    Button1 b1 = new Button1();
    // Button1 b2 = new Button1();
    // Button1 b3 = new Button1();
  }
}

 

객체지향 설계 5원칙-SOLID

SRP(Single Responsibility Principle)-단일 책임 원칙

하나의 모듈은 한 가지 책임을 가져야 한다(모듈이 변경되는 이유는 한 가지여야 한다)

해당 모듈이 여러 대상 또는 액터들에 대해 책임을 가져서는 안 되고, 오직 하나의 액터에 대해서만 책임을 져야한다

 

OCP(Open/Closed Principle)-개방 폐쇄 원칙

확장에 열려있다(요구사항이 변경될 때 새로운 동작을 추가하여 애플리케이션의 기능을 확장할 수 있다)

수정에 닫혀있다(기존의 코드를 수정하지 않고 애플리케이션의 동작을 추가하거나 변경할 수 있다)

개방 폐쇄 원칙을 지키기 위해선 추상화에 의존해야 함(추상화란 핵심적인 부분만 남기고, 불필요한 부분은 제거함으로써 복잡한 것을 간단히 하는 것) → 추상화를 통해 변하지 않는 부분만 남김으로써 기능을 구체화, 확장할 수 있음

객체의 결합도가 높아질수록 개방-폐쇄 원칙을 따르기 어려워짐 → 추상화를 통해 변하는 것들은 숨기고 변하지 않는 것들에 의존하게 하면 기존의 코드, 클래스들을 수정하지 않고 애플리케이션 확장 가능

 

LSP(Liskov Substitution Principle)-리스코프 치환 원칙

하위 타입은 상위 타입을 대체할 수 있어야 함

해당 객체를 사용하는 클라이언트는 상위 타입이 하위 타입으로 변경되어도, 차이점 없이 상위 타입의 퍼블릭 인터페이스를 통해 서브 클래스를 사용할 수 있어야 함

자식 클래스가 부모 클래스를 대체하기 위해선 부모 클래스에 대한 클라이언트의 가정을 준수해야 함

대체 가능성을 결정해야 하는 것은 해당 객체를 이용하는 클라이언트

 

ISP(Interface Segregation Principle)-인터페이스 분리 원칙

클라이언트의 목적과 용도에 적합한 인터페이스만을 제공하는 것

모든 클라이언트가 자신의 관심에 맞는 퍼블릭 인터페이스(외부에서 접근 가능한 메시지)만을 접근하여 불필요한 간섭을 최소화 → 기존 클라이언트에 영향을 주지 않고 객체의 기능 확장, 수정 가능

 

DIP(Dependency Inversion Principle)-의존관계 역전 원칙

고수준 모듈(변경이 없는 추상화된 클래스, 인터페이스)은 저수준 모듈(변하기 쉬운 구체 클래스)의 구현에 의존해서는 안 되며, 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 함

추상화에 의존하며 구체화에는 의존하지 않는 설계 원칙

 

package dev_java.week4;

import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class DeptTable7 extends JFrame implements ActionListener {
  // 선언부
  // 파라미터의 this는 DeptTable7 타입이고 main에서 호출된 생성자로
  // 현재 메모리에 로딩 중인 객체를 가리킨다
  // JTable7Dialog에서 부서번호, 부서명, 지역을 입력한 후 저장버튼을 누르면
  // Vector<String[]> 추가하고 추가된 로우를 포함하는 Vector가 부모창에
  // 새로 리프레쉬(재사용>부모클래스에 구현) 되어야 하니까 jtd를 호출해야함
  JTable7Dialog jtd7 = new JTable7Dialog(this);
  String header[] = { "부서번호", "부서명", "지역" };
  String datas[][] = new String[0][3];
  DefaultTableModel dtm_dept = new DefaultTableModel(datas, header);
  JTable jtb_dept = new JTable(dtm_dept);
  JScrollPane jsp_dept = new JScrollPane(jtb_dept,
      JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
      JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
  JPanel jp_north = new JPanel();
  JButton jtbn_sel = new JButton("조회");
  JButton jtbn_ins = new JButton("입력");
  JButton jtbn_upd = new JButton("수정");
  JButton jtbn_det = new JButton("상세보기");
  // String[] 벡터 선언 및 생성
  static Vector<String[]> vData = new Vector<>(); // vData.size() = 0

  // 생성자
  public DeptTable7() {
    initDisplay();
  }

  // 화면그리기
  public void initDisplay() {
    this.setDefaultCloseOperation(EXIT_ON_CLOSE);
    jp_north.setLayout(new FlowLayout(FlowLayout.RIGHT));
    // 버튼 색 설정
    jtbn_sel.setBackground(new Color(150, 100, 150));
    jtbn_sel.setForeground(Color.WHITE);
    jtbn_ins.setBackground(new Color(19, 99, 57));
    jtbn_ins.setForeground(Color.WHITE);
    jtbn_upd.setBackground(new Color(7, 84, 170));
    jtbn_upd.setForeground(Color.WHITE);
    jtbn_det.setBackground(new Color(250, 200, 50));
    jtbn_det.setForeground(Color.WHITE);
    // 버튼 추가
    jp_north.add(jtbn_sel);
    jp_north.add(jtbn_ins);
    jp_north.add(jtbn_upd);
    jp_north.add(jtbn_det);
    // 이벤트리스너 연결
    jtbn_sel.addActionListener(this);
    jtbn_ins.addActionListener(this);
    jtbn_upd.addActionListener(this);
    jtbn_det.addActionListener(this);
    // this(상속받은 JFrame)에 추가
    this.add("North", jp_north);
    this.add("Center", jsp_dept);
    this.setTitle("부서관리 시스템 Ver1.0");
    this.setSize(500, 400);
    this.setVisible(true);
  }

  // 새로고침-Vector에 담긴 String[] 출력하기
  // 입력, 수정화면에서 저장 버튼을 누르면 Vector에 String[]을 추가하고
  // 그 다이얼로그 화면은 닫히고 부모창은 새로고침 처리한다
  // 다이얼로그 창에서 부모클래스의 refreshData메소드를 호출해야함
  // 그러니 인스턴스화할 때 파라미터에 this를 넘겨서 사용할 수 있도록 할 것(NullPointerException발생하지 않도록)
  public void refreshData() {
    System.out.println("refreshData 호출");
    if (DeptTable7.vData.size() <= 0) {
      JOptionPane.showMessageDialog(this, "조회결과가 없습니다.", "WARNING", JOptionPane.ERROR_MESSAGE);
      return; // refreshData() 탈출
    }
    System.out.println("DeptTable7: " + vData.size());

    // 입력, 수정 전에 조회된 정보는 삭제함
    while (dtm_dept.getRowCount() > 0) {
      dtm_dept.removeRow(0);
    }

    // 벡터의 크기만큼 반복하면서 dtm_dept 데이터셋에 String[] 추가
    for (int i = 0; i < vData.size(); i++) {
      String[] oneRow = vData.get(i);
      dtm_dept.addRow(oneRow);
    }
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    Object obj = e.getSource();
    if (obj == jtbn_sel) { // 조회버튼을 눌렀을 때
      refreshData();

    } else if (obj == jtbn_ins) { // 입력버튼을 눌렀을 때
      jtd7.set("입력", true, null);

    } else if (obj == jtbn_upd) { // 수정버튼을 눌렀을 때
      // 어떤 row를 수정할지 확인
      // JTable 목록에서 선택한 로우의 index값을 가져옴
      int index = jtb_dept.getSelectedRow();
      // 데이터셋객체로 벡터를 사용중이니 벡터에서 꺼낸 값으로 String[] 초기화
      // 테이블의 양식 폼인 JTable 이벤트로 얻어옴
      String[] oneRow = vData.get(index);
      jtd7.set("수정", true, oneRow);

    } else if (obj == jtbn_det) { // 상세보기를 눌렀을 때
      int index = jtb_dept.getSelectedRow();
      String[] oneRow = vData.get(index);
      jtd7.set("상세보기", true, oneRow);
      jtd7.jtf_deptNo.setEnabled(false);
      jtd7.jtf_deptName.setEnabled(false);
      jtd7.jtf_deptLoc.setEnabled(false);
      jtd7.jbtn_save.setEnabled(false);
    }
  }

  // 메인메소드
  public static void main(String[] args) {
    new DeptTable7();
  }
}

 

package dev_java.week4;

import java.awt.FlowLayout;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;

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

public class JTable7Dialog extends JDialog implements ActionListener {
  // 선언부
  DeptTable7 dt7 = null;
  JPanel jp_center = new JPanel();
  JScrollPane jsp_center = new JScrollPane(jp_center);
  JPanel jp_south = new JPanel();
  JLabel jlb_deptNo = new JLabel("부서번호");
  JTextField jtf_deptNo = new JTextField(10);
  JLabel jlb_deptName = new JLabel("부서명");
  JTextField jtf_deptName = new JTextField(20);
  JLabel jlb_deptLoc = new JLabel("지역");
  JTextField jtf_deptLoc = new JTextField(20);
  // jp_south속지에 버튼 붙임
  JButton jbtn_save = new JButton("저장");
  JButton jbtn_close = new JButton("닫기");
  // String[] 선언
  String[] oneRow = null;

  // 생성자
  public JTable7Dialog(DeptTable7 dt7) {
    this.dt7 = dt7;
    initDisplay();
  }

  // 화면그리기
  public void initDisplay() {
    // 레이아웃 설정
    jp_center.setLayout(null);
    jp_south.setLayout(new FlowLayout(FlowLayout.RIGHT));
    jlb_deptNo.setBounds(20, 20, 80, 20);
    jtf_deptNo.setBounds(120, 20, 80, 20);
    jlb_deptName.setBounds(20, 45, 100, 20);
    jtf_deptName.setBounds(120, 45, 150, 20);
    jlb_deptLoc.setBounds(20, 70, 100, 20);
    jtf_deptLoc.setBounds(120, 70, 150, 20);
    jp_center.add(jlb_deptNo);
    jp_center.add(jtf_deptNo);
    jp_center.add(jlb_deptName);
    jp_center.add(jtf_deptName);
    jp_center.add(jlb_deptLoc);
    jp_center.add(jtf_deptLoc);
    jp_south.add(jbtn_save);
    jp_south.add(jbtn_close);
    jbtn_save.addActionListener(this);
    jbtn_close.addActionListener(this);
    this.add("Center", jsp_center);
    this.add("South", jp_south);
    this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    this.setTitle("상세보기");
    this.setSize(400, 360);
    this.setVisible(false);
  }

  // 각 컬럼(부서집합-부서번호, 부서명, 지역)의 값들을
  // 설정하거나 읽어오는 getter/setter 메소드
  public String getDeptNo() {
    return jtf_deptNo.getText();
  }

  public void setDeptNo(String deptNo) {
    jtf_deptNo.setText(deptNo);
  }

  public String getDeptName() {
    return jtf_deptName.getText();
  }

  public void setDeptName(String deptName) {
    jtf_deptName.setText(deptName);
  }

  public String getDeptLoc() {
    return jtf_deptLoc.getText();
  }

  public void setDeptLoc(String deptLoc) {
    jtf_deptLoc.setText(deptLoc);
  }

  // 아래 메소드는 DeptTable7에서 호출됨
  // actionPerformed에서 이벤트(입력, 수정, 상세보기)가 발생되면 호출됨
  // 메소드의 파라미터 자리는 Call by Value에 의해서 결정됨
  public void set(String title, boolean isView, String[] oneRow) {
    this.setTitle(title);
    this.setVisible(isView);
    this.oneRow = oneRow;
    setValue(oneRow);
  }

  public void setValue(String[] oneRow) {
    // 입력을 위한 윈도우 설정-모든 값을 빈문자열로 세팅
    // 상세조회, 수정시는 배열로 받은 값으로 세팅
    if (oneRow == null) {
      setDeptNo("");
      setDeptName("");
      setDeptLoc("");
    } else {
      setDeptNo(oneRow[0]);
      setDeptName(oneRow[1]);
      setDeptLoc(oneRow[2]);
    }
  } // end of setValue

  // 메인
  public static void main(String[] args) {
    new JTable7Dialog(null);
  }

  @Override
  public void actionPerformed(ActionEvent e) {
    Object obj = e.getSource();
    if (obj == jbtn_save) {
      // oneRow가 존재하면 수정모드, 그렇지 않으면 입력모드
      if (oneRow != null) { // 수정모드
        int index = dt7.jtb_dept.getSelectedRow();
        String[] oneRow = { jtf_deptNo.getText(), jtf_deptName.getText(), jtf_deptLoc.getText() };
        DeptTable7.vData.set(index, oneRow);
        dt7.refreshData();
        this.dispose();

      } else { // 입력모드
        String[] oneRow = { getDeptNo(), getDeptName(), getDeptLoc() };
        System.out.println(oneRow[0] + ", " + oneRow[1] + ", " + oneRow[2]);
        // System.out.println("before: " + DeptTable7.vData.size());
        DeptTable7.vData.add(oneRow);
        // System.out.println("after: " + DeptTable7.vData.size());
        dt7.refreshData();
        this.dispose();
      }
    } else if (obj == jbtn_close) {
      this.dispose();
    }
  }
}

 

package dev_java.week4;

import java.util.Map;
import java.util.Vector;

public class Vector1_2  {
  public static void main(String[] args) {
    // 2중(outter = Vector -> size(), inner = String[] -> length) for문으로 처리
    // Vector는 끼워넣기가 가능, 수정에대한 API제공은 없다-클라우드 기반 데이터베이스 제품들이 이런방식
    Vector<String[]> v = new Vector<>();
    // Vector<Map<String, Object>> v3 = new Vector<>(); // 실무 사용 패턴!
    // 현재 크기는 0
    String temp[] = { "10", "개발부", "서울" };
    v.add(temp); // 인덱스0에 끼워넣기
    temp = new String[] { "20", "총무부", "인천" };
    v.add(temp); // 인덱스1에 끼워넣기

    for (int i = 0; i < v.size(); i++) { // row의 수, 2번 반복됨
      String[] result = v.get(i);
      for (int j = 0; j < temp.length; j++) { // column의 수, 3번 반복됨
        System.out.print(result[j] + " ");
      }
      System.out.println();
    }

    // 단일 for문으로 처리 가능
    Vector<String> v2 = new Vector<>();
    v2.add("30");
    v2.add("영업부");
    v2.add("부산");
    for (int i = 0; i < v2.size(); i++) {
      System.out.print(v2.get(i) + " ");
    }
  }
}