상세 컨텐츠

본문 제목

팩토리 메소드를 interface로 구현해보기

카테고리 없음

by kail 2022. 2. 27. 02:19

본문

https://www.youtube.com/watch?v=qr7I18Lhsl8&list=PLsoscMhnRc7pPsRHmgN4M8tqUdWZzkpxY&index=2 

위 영상과 4강 팩토리 메소드를 보고, 의문이 들었다.

(따라서 소스예제는 3강에 있는 abstract단만 건들여봤다)

 

 

interface 자체는 protected같은 코드가 오기 어렵다

이런 경우 protected가 의도적으로 함수를 숨기기 위해서 사용되는 장점을 잃는 댓가가 따른다.

따라서, 자바에서는 위의 경우를 사용할 때 전체 코드에서 abstract를 지원해서, 기능을 확장하며, 사용하는걸 (컴파일러가) 권고한다..

우선 abstract 대신 interface로 저것도 대체 가능하지 않을까? 라는 의문이 들었고 이를 실제로 코드로 작성했다.

(네이밍은 이해바란다..)

우선 내가 interface로 시도하려고 했다가, 위와 같이 작성한 이유가 있는데, 그것 까지 코드 보여주면 너무 길어지기 때문에

간단히 말로 설명한다....

1. 큰 public class함수에 protected 인터페이스를 작성하고, 그 안을 public으로 작성(c++과는 다르기 때문에 인터페이스 안의 메소드는 제어 확장자가 정해져있기 때문에 이렇게 작성하게 된다...)

1-1. 인터페이스안의 함수를 static으로 작성하고, 구현체에서 그 값을 수정할 수 있도록 하기(try-catch로 컴파일 에러 예외처리 따로)

->이 경우 

    예외처리만 남은 상태였는데, SOLID 원칙 중 LSP(리스코프 치환 원칙)에 어긋나는 것 같아서 보류하고 바로 수정했다.

1-2.본문에서 시도한 것처럼 인터페이스의 함수를 none-static으로 하고, try-catch로 interface타입에 null을 부여하고,

각각 대입하기

(처음에 해봤을 때 타입이 잘 안나왔는데, stack-overflow를 보니 묶을 때 기본 타입이 안전하지 않을 수 있는건 null을 써야됨)

->이 경우에도, abstract보다 단점이 존재했다..

 

첫번째로, 객체 참조때문에 코드가 너무 길어짐.

둘째로, 그 타입을 상속받을 경우 @Override를 쓰지 못함(즉, 오버라이드 애너테이션에 맞지 않음)

 

    //하위 클래스로 나눠진 메소드들을 구현한다 (Templete method는 이걸 묶어서 다 구현)
    // protected interface t{
    //     public String fssdef();
    // }
    //자바에서 interface 에 정의되는 메소드는 외부로 공개되는 메소드를 정의하는 것 입니다. 그러므로 private 나 protected 로 정의가 불가능합니다.

    abstract class AbstGameConnectHelper {
        protected abstract String doSecurity(String string);
        protected abstract boolean authentication(String id,String password);
        protected abstract int authentication(String userName);
        protected abstract String connection(String info);

        //템플릿 메소드
        public String requestConnection(String encodedInfo) throws Exception {
            //보안 작업 -> 암호화된 문자열을 디코딩
            String decodedInfo = doSecurity(encodedInfo);
            String id="aaa";
            String password="bbb";

            if(!authentication(id,password)){   //id,password불일치라면
                throw new Error("아이디 암호 불일치");
            }

            String userName=""; 
            int i=authentication(userName);
            switch(i)
            {
                case -1:
                    throw new Exception("셧다운");
                case 0: //게임 매니저
                    break;
                case 1: //유료회원
                    break;
                case 2: //무료회원
                    break;
                case 3: //권한없음
                    break;
                default:    //기타 상황
                    break;
            }


            return connection(encodedInfo);

        }
    }

    class DefaultGameConnectHelper extends AbstGameConnectHelper {

        @Override
        protected String doSecurity(String string) {
            // TODO Auto-generated method stub
            System.out.println("디코드");
            return string;
        }

        @Override
        protected boolean authentication(String id, String password) {
            // TODO Auto-generated method stub
            System.out.println("아이디/암호 확인 과정");
            return true;
        }

        @Override
        protected int authentication(String userName) {
            // TODO Auto-generated method stub
            System.out.println("권한 확인");
            return 0;
        }

        @Override
        protected String connection(String info) {
            // TODO Auto-generated method stub
            System.out.println("마지막 접속단계!");
            return info;
        }

    }

 기존 코드

 

public class java32 {
    @FunctionalInterface
    protected interface faeaw{
        public String doSecurity2(String string);
    }
    @FunctionalInterface
    protected interface faeaw2{
        public boolean authentication2(String id,String password);
    }
    @FunctionalInterface
    protected interface faeaw3{
        public int authentication2(String userName);
    }
    @FunctionalInterface
    protected interface faeaw4 {
        public String connection2(String info);
    }

    public String requestConnection2(String encodedInfo) {
    try{
            // 보안 작업 -> 암호화된 문자열을 디코딩
    faeaw trySecurity=null;
    faeaw2 tryAuthentication2ToLogin=null;
    faeaw3 tryAuthentication2ToName=null;
    faeaw4 dfawef=null;
  
    String decodedInfo = trySecurity.doSecurity2(encodedInfo);
    

    String id="aaa";
    String password="bbb";
    if(!tryAuthentication2ToLogin.authentication2(id,password)){   //id,password불일치라면
        throw new Error("아이디 암호 불일치");
        }
            String userName=""; 
            int i=tryAuthentication2ToName.authentication2(userName);
            switch(i)
            {
                case -1:
                    throw new Exception("셧다운");
                case 0: //게임 매니저
                    break;
                case 1: // 유료회원
                    break;
                case 2: // 무료회원
                    break;
                case 3: // 권한없음
                    break;
                default:    // 기타 상황
                    break;
            }


            return dfawef.connection2(encodedInfo);

        }
        catch(Throwable e)
        {
            e.printStackTrace();
        }
        
        return null;
    }
}



class DefaultGameConnectHelper2 extends java32 {

        class wfnke implements faeaw { 
        @Override
            public String doSecurity2(String string) {
            // TODO Auto-generated method stub
            System.out.println("디코드");
            return string;
            }   
        }
            class wfnke2 implements faeaw2 {
            @Override
            public boolean authentication2(String id, String password) {
            // TODO Auto-generated method stub
                System.out.println("아이디/암호 확인 과정");
                return true;
            }
        }
        class wfnke3 implements faeaw3 {
            @Override
            public int authentication2(String userName) {
            // TODO Auto-generated method stub
            System.out.println("권한 확인");
            return 0;
            }
        }   

        class wfnke4 implements faeaw4 {
            @Override
            public String connection2(String info) {
            // TODO Auto-generated method stub
            System.out.println("마지막 접속단계!");
            return info;
        }
    }
}

implements로 구현한 코드

 

2번에 서술된 단점

class fawamewlkmf extends DefaultGameConnectHelper2{
    class Appl43te extends DefaultGameConnectHelper2.wfnke3 {
        // @Override 위의 타입에서 수정했으므로 이건 하면 안됨
    public String connection2(String info) {
        // TODO Auto-generated method stub
        System.out.println("??");
        System.out.println("마지막 접속단계!");
        return info;
        }
    }
}

아래와 같이 접근할 때 어렵게 접근해야한다는 단점이 존재한다..

class Fwln extends SFe {

	@Override
	protected String fanwk() {
		// TODO Auto-generated method stub
		return null;
	}
	
}
class fnewk extends Fwln {
	@Override
	protected String fanwk() {
		// TODO Auto-generated method stub
		return null;
	}
}

반면 abstract는 한번 더 상속을 받을 때 이렇게 쉽게 접근 할 수 있다.

후기는 위와 같이 @FunctionalInterface같은 에너테이션과 SOLID 원칙(isp,Ocp)에 위반되지 않는 코드를 작성하느라 힘들었고,

static으로 할 것이냐, non-static으로 할 것이냐, 다른 방법이 있느냐, interface 접근을 어케할 것이냐

이런 경우는 어떻게 처리하면 되느냐, 등 조건이 너무 많았다...

1번을 시도 할 때, String대신 Optional<String>을 쓰다가 본문에서는 쓰지 않았는데, 기존 코드에서 id, password에 null 값이 들어올 수도 있다는 내용이 없기도 했고, 이 경우 초기 선언 값을 Null로 했기 떄문에, 중간 인터페이스 과정이 NULL 값이 접근이 되면 예외가 될 것이고, 그게 아니면 기존 코드와 동일한 효과를 내기 때문에 쓰지 않았다.

개인적으로 Optional,람다를 익힌지 얼마 안되서 짜보고 싶긴 했는데, 아직은 딱히 쓰면 좋겠다는 코드가 잘 보이지 않은 것 같다