보조 스트림은 다른 스트림과 연결돼서 문자 변환, 성능 향상 등 다양한 기능을 제공하며,

다른 보조 스트림과 체인처럼 연결되어 사용될 수 있습니다.

만약 보조 스트림을 사용하게 된다면 안쪽 보조 스트림을 가장 먼저 자원해제 해야합니다.

 

 


 

 

1. 문자 변환 보조 스트림

 

문자 변환 보조 스트림은 바이트를 문자로 변환하는 스트림입니다.

 

public class InputStreamReaderExample {

	
    public static void main(String[] args) throws IOException {
        // 바이트 기반의 기본 입력 스트림 생성
        InputStream is = System.in; // 표준입력을 변수에 저장
		
        // 기본 입력 스트림을 문자 기반의 입력 스트림(Reader)로 변환
        // 즉, 기본 스트림에 보조 스트림을 사용함
        Reader reader = new InputStreamReader(is);
		
        // 보조스트림을 사용했으므로, 문자 기반으로 입력 수행
        int readCharNo;
        char[] cbuf = new char[100]; // 문자 기반 배열 생성
		
        // EOF(스트림의 끝)를 만날때까지, 문자기반의 입력 수행
        while ((readCharNo = reader.read(cbuf)) != -1) { // EOF(-1)
            // cbuf 크기만큼의 문자들을 읽어서, 문자열 조각 생성
            String data = new String(cbuf, 0, readCharNo);
            // 문자열 조각 출력
            System.out.println(data);
        } // while
		
        // 보조 스트림 자원 해제 후, 기본 스트림 자원 해제
        reader.close(); // 보조
        is.close();     // 기본
		
    } // main

} // end class

 

사용자가 입력한 내용을 그대로 출력하는 코드입니다.내용을 입력하고 엔터를 누르면 보조스트림이 바이트 코드로 변환 후 프로세스에 전달합니다.

 


 

여기서 윈도우 운영체제에서는 엔터를 누를 때 발생하는 두 개의 문자가 있습니다.

 

  1. Carriage Retrun (CR) : 현 행의 가장 처음 칼럼으로 이동
  2. Line Feed (LF) : 다음 로우로 이동

 

즉, 엔터키를 누르면 CR이 발생하여 현 행의 가장 처음 칼럼으로 이동 후 LF가 발생해 바로 다음 로우로 이동해서 줄 변환이 이루어집니다.

 

반면, Linux나 Unix, Mac 등 다른 운영체제에서는 LF, CR중 1개의 문자만 발생합니다.

 

 


 

다만, 위 코드에서는 CR/LF에 대한 처리를 따르 하고있지 않기 때문에, 사용자가 텍스트 입력 후 엔터를 눌렀다면,

CR/LF도 출력 결과에 포함되어 나오게 됩니다.

 

그렇기 때문에, 입력한 각 줄이 새로운 줄에 출력됩니다.

 

 

 

 

2. 성능 향상 보조 스트림

 

기본 스트림의 입출력 성능을 향상시키는 보조 스트림입니다.

 

네트워크의 버퍼는 데이터의 조각인 패킷을 임시로 저장하는 공간입니다.

이와 유사하게 성능 향상 보조 스트림도 버퍼 기반으로 동작을 수행하는데, 이는 데이터를 한번에 쓰고 읽는데 효율적입니다.

다만, 버퍼의 크기를 너무 크거나 작게 설정하면 오히려 성능 저하를 일으킬 수 있기 때문에 여러 요인을 고려해서 신중하게 버퍼의 크기를 선택해야 합니다.

 

@log4j2
public class FileOutputStreamExample {

	
    public static void main(String[] args) throws Exception{
        long start = 0;
    	long end = 0;
        String sourcePath = "C:/Temp/WINDOWS.X64_193000_db_home.zip";
        String targetPath = "C:/Temp/WINDOWS.X64_193000_db_home_target.zip";
		
        @Cleanup
        FileInputStream fis = new FileInputStream(sourcePath);
        BufferedInputStream bis = new BufferedInputStream(fis);
        @Cleanup
        FileOutputStream fos = new FileOutputStream(targetPath);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
		
		
        // 바이트 배열(100바이트)을 생성 후
        // 소스 파일에 대한 입력스트림에서 바이트들을 읽어서,
        // 타겟 파일에 대한 출력스트림(원래는 출력 버퍼)에 출력하여
        // 복사 기능을 수행
        int readByteNo;	// 한번 바가지로 읽을 때마다, 실제 읽어낸 바이트의
                        // 갯수를 저장할 변수 선언
		
        // 입출력 용도로 사용할 바이트 배열(100바이트) 생성
        byte[] readBytes = new byte[500];
		
        // 입력 버퍼로부터 바가지 크기만큼 읽어내고,
        // 입력 버퍼의 EOF(스트림의 끝)을 만날 대까지 실제로 읽어낸 바이트들을
        // 출력
        
        // ---------------------------------
        start = System.currentTimeMillis();
        // ---------------------------------
        
        while((readByteNo = bis.read(readBytes)) != -1) {   // 복사 기능 수행
            bos.write(readBytes, 0, readByteNo);            // 실제 읽어낸 바이트 개수 사용
        } // while
		
        // ---------------------------------
        end = System.currentTimeMillis();
        // ---------------------------------
        
        // 출력 버퍼 플러싱
        bos.flush();;
		
        log.info("time : {}", end - start);
    } // main
	
} // end class

 

성능 향상 보조 스트림 미사용 시

 

성능 향상 보조 스트림 사용 시

 

성능 향상 보조 스트림의 사용 유무에 따라 효율이 크게 차이나게 됩니다.

 

 

'Java' 카테고리의 다른 글

레코드  (0) 2023.09.06
객체 입출력 스트림  (0) 2023.08.18
파일 클래스  (0) 2023.08.17
Exception Handling  (0) 2023.07.28
Exception  (0) 2023.07.27

 

 

 

File 클래스는 파일 크기, 파일 속성, 파일 이름 등의 정보와 파일 생성 및 삭제의 기능을 제공합니다.

 

 


 

 

먼저 파일 객체를 생성하고 주소를 지정합니다.

저는 현재 Temp 폴더에 file.txt 파일이 없습니다.

 

@Log4j2
public class FileExample {

    // File 객체를 이용해서, CMD의 dir 명령어의 출력결과를 만듬
    public static void main(String[] args) throws IOException{
        // 파일 객체 생성 시, 생성자 매개변수로 File 객체가 표현할
        // 파일이나 디렉터리의 경로를 명시해주면 됨
        // 이때, 지정된 파일이나 디렉토리는 실제 존재하지 않아도 오류가 나지 않음.
        File dir = new File("C:/Temp/Dir");
        File file1 = new File("C:/Temp/file1.txt");
        File file2 = new File("C:/Temp/file2.txt");
        // File file3 = new File("file:///C:/Temp/file3.txt");
		
        log.info("1. dir : {}", dir);
        log.info("1. dir : {}", file1);
        log.info("1. dir : {}", file2);

 

file.txt

 

참조변수가 해당 레퍼런스를 참조하기 때문에, 해당 레퍼런스에 파일이 없어도 잘 출력이 되는 것을 확인할 수 있습니다.

 

 

그렇다면 File 클래스의 메서드를 활용해서 디렉터리를 생성하겠습니다.

 

        // 파일 생성
        // mkdirs는 file 객체가 가지고 있는 전체 경로 상에
        // 존재하지 않는 모든 디렉터리를 만들라는 의미임
        // 즉, 없으면 만들고 있으면 그대로 둠
        if(dir.exists() == false) {
			
        //  dir.mkdir();
            dir.mkdirs();
        } // if
		
        // 없으면 새로운 파일 생성
        if(file1.exists() == false) {
            file1.createNewFile();
        } // if
		
		
        if(file2.exists() == false) {
            file2.createNewFile();
        } // if

 

위 코드에서 exists()는 파일 존재의 유무를 확인하는 메서드입니다.

해당 경로에 파일이 없으면 만들고 있다면 변경사항은 없습니다.

 

 

위 코드들을 활용해서 C드라이브의 Temp폴더의 디렉터리 목록을 보여주는 코드를 작성할 수 있습니다.

 

        File temp = new File("C:/Temp");
		
        // 날짜 객체로부터 포매팅 문자열 생성
        SimpleDateFormat sdf = 
                new SimpleDateFormat("yyyy/mm/dd   a   HH:mm");
		
        // C:/temp 폴더 안에 있는 모든 파일을 배열로 획득
        File[] contents = temp.listFiles();
		
        // 실제 dir 명령의 출력 결과를 재현하는 코드
        System.out.println("    날짜             시간       형태           크기     이름");
        System.out.println("------------------------------------------------------------");
		
        for(File file : contents) {
			
            // 파일/디렉터리의 최종 수정 시간을 얻고, 이를 포매팅 문자열처럼 출력
            System.out.print(sdf.format(new Date(file.lastModified())));
			
            if(file.isDirectory()) {
                // 만약 디렉터리이면 디렉터리 표시기호를 만들어 넣어주고
                System.out.print("\t<DIR>\t\t\t" + file.getName());
            } else {
                // 만약 파일이면 파일 크기와 이름을 표시
                System.out.println("\t\t\t" + file.length() + "\t" + file.getName());
            } // if-else
			
            System.out.println();
        } // enhanced for
		
    } // main
	
} // end class

 

 

 

dir

 

코드 자체는 크게 어렵지 않습니다.

for문은 temp 폴더 내 디렉터리, 파일을 하나씩 순회하며 콘솔에 출력하는 역할입니다.

위 코드는 명령 프롬포트의 명령어인 dir의 결과와 유사한 것을 알 수 있습니다.

 

 

'Java' 카테고리의 다른 글

객체 입출력 스트림  (0) 2023.08.18
보조 스트림  (0) 2023.08.17
Exception Handling  (0) 2023.07.28
Exception  (0) 2023.07.27
익명구현객체  (0) 2023.07.26

 

 

 

GROUP BY(그룹화)는 SELECT 절에서 집계함수와 단순칼럼이 복합적으로 선택될 때 사용됩니다.

 

 


 

 

1. GROUP BY

 

GROUP BY절은 NULL을 제외하지 않고 그룹화하여 로우의 가장 아래에 생성합니다.

 

먼저 SELECT 절에 스칼라를 선택하는 쿼리를 작성하였습니다.

스칼라는 쿼리의 결과가 하나인 것을 의미합니다.

 

SELECT
    MAX(salary)
FROM
    employees;

 

스칼라

 

결과에서도 알 수 있듯이,

집계함수가 단독적으로 사용된다면 그룹화는 필요 없습니다.

 

 

하지만 집계함수와 단순칼럼이나 표현식이 함께 사용된다면 GROUP BY 절이 사용되야 합니다.

고용된 날짜 별 총 급여를 쿼리로 작성하였습니다.

 

SELECT
    TO_CHAR(hire_date, 'YYYY/MM') AS 고용연월,
    SUM(salary)   AS "총 급여"
FROM
    employees
GROUP BY
    TO_CHAR(hire_date, 'YYYY/MM')
ORDER BY
    1 ASC;

 

위 쿼리는 TO_CHAR를 그룹화하여 해당 고용날짜에 해당하는 사원의 총 급여에 대한 쿼리입니다.

만약 SELECT절에 추가적인 칼럼이나 표현식이 선택되면 GROUP BY절에서 추가적인 그룹화가 필요합니다.

 

 

 

2. HAVING

 

HAVING절은 그룹화와 함께 사용됩니다.

HAVING절은 GROUP BY를 통해 그룹화하고 로우(row, 행)가 아닌 그룹을 제외시킬 수 있습니다.

또한 WHERE절에서는 사용이 불가능한 집계함수를 HAVING절에서는 사용할 수 있습니다.

 

SELECT
    TO_CHAR(hire_date, 'YYYY/MM') AS 고용연월,
    SUM(salary)   AS "총 급여"
FROM
    employees
GROUP BY
    TO_CHAR(hire_date, 'YYYY/MM')
HAVING
    SUM(salary) >= 10000
ORDER BY
    1 ASC;

 

위 쿼리처럼 HAVING절은 집계함수 사용이 가능하며, WHERE절과 같은 그룹에 대한 필터링을 수행합니다.

 

 

 

 

 

 

 

 

'Database > SQL' 카테고리의 다른 글

정규화  (0) 2023.09.06
외부 조인  (0) 2023.08.21
인라인 뷰  (0) 2023.08.17
외래 키  (1) 2023.08.16
데이터 사전  (0) 2023.08.16

 

 

 

인라인 뷰는 기존 테이블에서 필요한 칼럼만 추출 후 일종의 임시 테이블로 사용하는 방식입니다.

 

 


 

 

인라인 뷰를 사용하면 쿼리가 더 간결해지고 중복 코드를 제거할 수 있습니다.

다만, 과도하게 중첩된 인라인 뷰는 쿼리의 복잡도를 높일 수 있기 때문에 주의해야 합니다.

 

 

간단한 예시 코드입니다.

 

SELECT
    부서번호,
    평균월급
FROM
    ( SELECT
          department_id AS 부서번호,
          AVG(salary)   AS 평균월급
      FROM
          employees
      WHERE
          department_id IS NOT NULL
      GROUP BY
          department_id
      ORDER BY
          department_id ASC;
     );

 

위 쿼리에서 FROM 절에 해당하는 부분이 ' 인라인 뷰 ' 입니다.

메인 쿼리인 SELECT 절에서는 인라인 뷰의 쿼리 결과를 별칭을 선택해서 받아왔습니다.

 

 

 

 

'Database > SQL' 카테고리의 다른 글

외부 조인  (0) 2023.08.21
그룹화  (0) 2023.08.17
외래 키  (1) 2023.08.16
데이터 사전  (0) 2023.08.16
크로스 조인  (0) 2023.08.10

 

 

 

FORGEIGN KEY(외래 키 또는 참조 키)는 자식 테이블에서 부모 테이블을 참조할 때,

올바른 데이터만 참조하도록(참조 무결성) 제약합니다.

 

 


 

 

외래 키는 부모 테이블과 자식 테이블 간 참조 무결성을 위한 제약 조건이므로, 

자식 테이블에서 참조하는 칼럼을 부모 테이블에서 PRIMARY KEY 또는 UNIQUE로 설정해야 합니다.

 

 

부모 테이블을 만들고 자식 테이블에서 참조할 칼럼에 제약 조건을 설정하고, 로우를 입력합니다.

 

START

    CREATE TABLE dept (
        deptno  NUMBER(2)     CONSTRAINT dept_deptno_pk PRIMARY KEY,
        dname   VARCHAR2(15),
        loc     VARCHAR2(15)
    );

    INSERT INTO dept02 (deptno, dname, loc)
    VALUES (10, '인사', '서울');


    INSERT INTO dept02 (deptno, dname, loc)
    VALUES (20, '개발', '광주');


    INSERT INTO dept02 (deptno, dname, loc)
    VALUES (30, '관리', '부산');


    INSERT INTO dept02 (deptno, dname, loc)
    VALUES (40, '영업', '경기');

    COMMIT

END;

 

 

이제 자식 테이블을 만들고, 로우를 입력합니다.

 

START
    
    CREATE TABLE emp02 (
        empno   NUMBER(4)       CONSTRAINT emp02_empno_pk PRIMARY KEY,
        ename   VARCHAR2(15),
        deptno  NUMBER(2)       CONSTRAINT emp02_deptno_fk REFERENCES dept02(deptno)
    );


    INSERT INTO emp02 (empno, ename, deptno)
    VALUES (1000, 'John', 10);


    INSERT INTO emp02 (empno, ename, deptno)
    VALUES (2000, 'Smith', 20);


    INSERT INTO emp02 (empno, ename, deptno)
    VALUES (3000, 'Sam', NULL);


    -- ORA-02291: integrity constraint (SCOTT.EMP02_DEPTNO_FK) violated - parent key not found
    -- INSERT INTO emp02 (empno, ename, deptno)
    -- VALUES (4000, 'Mike', 50);

    COMMIT

END;

 

 

이제 각 테이블의 제약 조건을 데이터 사전을 통해 조회합니다.

 

SELECT
    table_name,
    constraint_type,
    constraint_name,
    r_constraint_name
FROM
    user_constraints
WHERE
    table_name IN ('DEPT02', 'EMP02')
ORDER BY
    table_name ASC;

 

FORGEIGN KEY

 

위의 결과에서, emp02 테이블의 참조키는 dept02 테이블의 기본키를 참조하는 것을 알 수 있습니다.

 

 


 

 

특정 칼럼을 참조하고 있는 자식이 있으면 해당 칼럼을 삭제할 수 없습니다.

만약 부모 테이블의 칼럼을 삭제하고 싶다면,

테이블 작성 시, FK에 ON DELETE CASCADE 키워드나 ON DELETE SET NULL 키워드를 사용하면 됩니다.

 

CREATE TABLE emp02 (
    empno NUMBER(4)     CONSTRAINT emp02_empno_pk PRIMARY KEY,
    ename VARCHAR(15),
    deptno NUMBER(2)
        CONSTRAINT emp02_deptno_fk REFERENCES dept02(deptno) ON DELETE CASCADE
);

-- ON DELETE CASCADE --
-- 참조하는 부모 테이블의 로우가 삭제되면, 해당 로우를 참조하는 자식 테이블의 로우도 연쇄 삭제됨 --



CREATE TABLE emp03 (
    empno NUMBER(4)     CONSTRAINT emp02_empno_pk PRIMARY KEY,
    ename VARCHAR(15),
    deptno NUMBER(2)
        CONSTRAINT emp03_deptno_fk REFERENCES dept02(deptno) ON DELETE SET NULL
);

-- ON DELETE SET NULL --
-- 참조하는 부모 테이블의 로우가 삭제되면, 해당 로우를 참조하는 자식 테이블의 필드값은 NULL로 바뀜--

 

 

 

 

'Database > SQL' 카테고리의 다른 글

그룹화  (0) 2023.08.17
인라인 뷰  (0) 2023.08.17
데이터 사전  (0) 2023.08.16
크로스 조인  (0) 2023.08.10
동등 조인  (0) 2023.07.26

 

 

 

SQL은 데이터 사전을 통해 제약 조건을 확인할 수 있습니다.

 

 


 

CREATE TABLE

 

데이터 사전을 조회하기 전, 실습용 테이블을 생성하였습니다.

 

CREATE TABLE department (
    deptno NUMBER(2)    CONSTRAINT department_deptno_pk PRIMARY KEY,
    dname  VARCHAR2(15),
    loc    VARCHAR2(15)
);

-- 테이블 명만 다른 동일한 쿼리입니다. --

CREATE TABLE department2 (
    deptno NUMBER(2),
    dname  VARCHAR2(15),
    loc    VARCHAR2(15),
    
    CONSTRAINT department2_deptno_pk PRIMARY KEY (deptno)
);

 

칼럼에 제약 조건을 걸 때, 해당 로우에 제약 조건 쿼리를 작성할 수 있지만,

department2처럼 칼럼을 먼저 작성하고, 아래에 제약 조건 쿼리를 작성하여 지정할 수 있습니다.

 

 


 

 

1. USER_CONSTRAINTS

USER_CONSTRAINTS 데이터 사전은 특정 테이블의 제약 조건을 확인할 수 있지만,

어떤 칼럼에 제약 조건이 설정되어 있는지는 확인 불가합니다.

 

다음은 제약 조건의 타입입니다.

 

  • P : PRIMARY KEY (기본키)
  • R : FOREIGN KEY (외래키)
  • U : UNIQUE (고유키)
  • C : NOT NULL, CHECK ( 필드의 NULL값 제한, 칼럼에 특정 제약 조건 설정 )

 

 

다음은 department, department2 테이블의 USER_CONSTRAINTS 데이터 사전 쿼리입니다.

 

SELECT
    *
FROM
    USER_CONSTRAINTS
WHERE
    table_name IN ('DEPARTMENT', 'DEPARTMENT2');

 

USER_CONSTRAINTS

 

앞서 두 테이블의 deptno 칼럼을 PRIMARY KEY(기본키)로 제약 조건을 설정했기 때문에, 

CONSTRAINT TYPE이 P로 나왔습니다.

 

 

 

2. USER_CONS_COLUMNS

USER_CONSTRAINTS 데이터 사전은 특정 테이블의 제약 조건을 확인했다면,

USER_CONS_COLUMNS는 어떤 칼럼에 제약 조건이 설정되어 있는지 확인할 수 있습니다.

SELECT
    *
FROM
    USER_CONS_COLUMNS
WHERE
    table_name IN ('DEPARTMENT', 'DEPARTMENT2');

 

USER_CONS_COLUMNS

 

쿼리 결과처럼 deptno 칼럼이 PRIMARY KEY(기본키)로 설정되어 있음을 볼 수 있습니다.

 

 

데이터 사전을 통해 조회를 할 때, 분별력을 부여하기 위해

CREATE TABLE시, CONSTRAINT NAME을 잘 작성해야 합니다.

일반적으로 " 테이블 명_칼럼 명_키 타입" 으로 작명합니다.

 

 

 

 

 

 

 

 

'Database > SQL' 카테고리의 다른 글

그룹화  (0) 2023.08.17
인라인 뷰  (0) 2023.08.17
외래 키  (1) 2023.08.16
크로스 조인  (0) 2023.08.10
동등 조인  (0) 2023.07.26

 

 

 

프록시 패턴(Proxy Pattern)은 호출에 대한 흐름제어가 목적입니다.

의존 역전 원칙(Dependency Inversion Principle, DIP)를 활용한 디자인 패턴입니다.

 

 


 

의존 역전 원칙(Dependency Inversion Principle, DIP)

DIP는 상위 객체가 하위 객체에 의존해서는 안되며 추상화에 의존해야함을 의미합니다.

 

즉, DIP는 상위 객체가 하위 객체에 직접 의존하기보다는 인터페이스나 추상 클래스를 통해 객체에 의존하라는 것입니다.

 

인터페이스를 통해 하위 객체에 접근하게 되면상위 객체와 하위 객체 간 관계가 느슨해지고 이로 인해  코드의 유연성을 높일 수 있습니다.

 

 

 

프록시 패턴(Proxy Pattern)

 

네트워크에서는 웹 프록시가 존재합니다.

웹 프록시를 간단하게 설명하면,

클라이언트와 서버 사이에서 중개자의 역할을 하면서 부가적인 기능을 통해 로직을 수행합니다.

 

이와 비슷하게, 프록시 패턴은 중개자의 역할이며

실제로 객체가 필요할 때까지 생성을 지연하고 접근을 제어하는 것에 목적을 두고 있습니다.

 

이해를 돕기 위한 간단한 예시 코드입니다.

 

public class IService {
    abstract String runSomething();
    
} // end interface

 

public class Service implements IService {
	
    @override
    public String runSomething() {
    	return "runSomething()"
        
    } // runSomething
    
} // end class

 

Service 클래스는 IService 인터페이스를 구현하고 있고 있습니다.

이제 Service 클래스에 접근을 제어하기 위한 Proxy 클래스를 만들겠습니다.

 

@log4j2
public class Proxy implements IService {
    IService service1;
    
    
    @override
    public String runSomething() {
    	log.trace("Proxy runSomething() Method invoked.");
        
        service1 = new Service();  // IService를 참조하는 Service 객체 생성
        return service1.runSomething();
        
    } // runSomething
    
} // end class

 

위 코드는 IService 인터페이스를 구현하는 Proxy 클래스입니다.

 

@log4j2
public class ClientWithProxy {
    
    
    public static class main(String[] args) {
        log.trace("main({}) invoked.", Arrays.toString(args));
        
        
        IService proxy = new Proxy(); // IService를 참조하는 Proxy 객체 생성
        log.info(proxy.runSomething());
    
    } // main
    
} // end class

 

 

Proxy 클래스를 보면

runSomething() 메서드를 실행시켜야 Service 객체를 생성하고 이용할 수 있도록 하였습니다.

 

만약 여기서 실행 클래스가 이용할 Service 클래스가 늘어난다 하더라도 Proxy 클래스를 이용해 객체를 관리하고 접근할 수 있습니다.

 

 

 

 

'Java > Design Patterns' 카테고리의 다른 글

암호화 알고리즘  (0) 2023.09.14
어댑터 패턴  (0) 2023.08.13

 

 

 

어댑터 패턴은 개방 폐쇄 원칙(Open/Closed Principle, OCP)을 활용한 패턴입니다.

OCP에 대해 먼저 이해하고 어댑터 패턴에 대해 다루겠습니다.

 


 

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

 

OCP는 자바 개발자라면 의식하지 않아도 항상 접할 수 있는 환경에 놓여져 있습니다.

 

자바 개발자는 코드를 작성할 때,

이 코드가 Window, MacOS, Linux 등 어느 운영체제에서 쓰일지 하나하나 고려하며 작성하는 개발자는 없습니다.

 

이러한 걱정을 덜어주는 이유는 JVM이라는 존재 덕분입니다.

 

JVM으로 인해 개발자는 내가 작성하는 소스코드가 어느 운영체제에 쓰일지 신경쓰지 않아도 되기 때문에 운영체제의 변화에 닫혀있다고 할 수 있습니다.

반면, JVM은 새로운 운영체제나 기존의 운영체제의 변화에 따라 업데이트됩니다. 이러한 특징은 JVM이 운영체제의 확장에 열려있다고 할 수 있습니다.

 

즉, OCP는 클래스같은 소프트웨어 엔티티는 확장(extends)에 열려있어야 하지만 변화에는 닫혀있어야 한다는 원칙입니다.

 


 

어댑터 패턴(Adepter Pattern)

 

마트에 방문한 손님은 물품을 구매합니다.

마트의 포스기는 제품들의 재고상황, 신제품, 없어진 제품 등을 관리합니다.

손님은 포스기 덕분에 마트의 제품들에 대한 변화에 영향을 받지 않고 편하게 제품을 구매할 수 있습니다.

 

@log4j2
public class Product1{
    
    
    void purchase1{
    	log.info("1번 제품을 구매하였습니다.");
    } // purchase1
    
} // end class

 

@log4j2
public class Product2{
    
    
    void purchase2{
    	log.info("2번 제품을 구매하였습니다.");
    } // purchase2
    
} // end class

 

위 코드는 손님이 구매할 제품군입니다. 제품을 구매하기 위해 포스기가 있어야 합니다.

 

 

@log4j2
public class Pos{
    Purchase1 p1 = new Purchase();
    
    void runService1{
    	p1.purchase1;
    } // purchase1
    
     void runService2{
    	p2.purchase2;
    } // runService2
    
} // end class

 

제품을 구매할 포스기가 구비되었으니 이제 손님은 포스기를 이용해 제품을 구매합니다.

 

 

@log4j2
public class Consumer{
	
    
    public static class main(String[] args){
    	Pos pos = new Pos();
        
        
        pos.runService1();  // 1번 제품을 구매하였습니다.
        pos.runService2();  // 2번 제품을 구매하였습니다.
        
    } // main
    
} // end class

 

 

어댑터 패턴은 간단히 말해,

호출자가 변환기(Converter)를 이용해 피호출자를 호출하는 방식의 디자인 패턴입니다.

 

 

'Java > Design Patterns' 카테고리의 다른 글

암호화 알고리즘  (0) 2023.09.14
프록시 패턴  (0) 2023.08.13

+ Recent posts