53. Facade 패턴(퍼사드)적용 (front와 비슷)
- GoF의 Facade 패턴의 동작원리 이해와 적용하는 방법
- ActionListener 실행에 Facade 객체를 사용하기
1. 기존 구조
- client는 여러 개의 객체에 의존한다. => 의존 객체 변경의 영향을 자주 받는다. => 개선 방법: "Low Coupling" 유지 (다른 객체와의 관계를 줄이는 것)
* GRASP(일반적인 책임할당 소프트웨어 패턴)의 Low Coupling
2. 개선된 구조
Facade: 앞 단에서 Client의 모든 요청을 받아 객체에 전달 & - 여러 객체에서 수행하는 공통 작업을 담당
(=Front Controller)
Client는 Facade 역할의 객체만 사용. 뒷 단의 객체를 사용하는 것은 Facade 뿐 ! => 뒷 단의 객체에 변화가 발생하더라도 클라이언트는 영향을 받지 않는다.
--------
프로젝트 적용
Server util에 Dispatcher 클래스 추가
Serverapp 수정
menu 수정
Dispatcher 수정
menugroup add 메서드 오버로딩 하고 서버앱 수정
54. Ioc 컨테이너 적용하기
IoC(Inversion of Control) 제어의 역전 = 역제어
1. Dependency Injection (의존 객체 주입 = 의존성 주입)
일반: 필요한 객체를 만들어 쓴다.
역제어: 필요한 객체를 외부에서 꽂아준다. => 교체가 쉽다. 큰(복잡한) 시스템에 적합. 목업(mockup 가짜)개체를 주입하여 단위 테스트를 쉽게 할 수 있다.
2. Event Listener
일반: 위 -> 아래 순차적으로 코드를 실행
역제어: 특정 이벤트가 발생했을때 등록된 리스너가 실행된다.
" 실행 흐름이 역행한다."
IoC 컨테이너 = Bean Container + Dependency Injection
Bean : Object(객체)의 애칭.// java(coffee) Object(bean)
Dispatcher Listener 하는 일
1. Listener 객체 준비
2. Listener의 Facade 역할
프로젝트 적용
util- ApplicationContext class 준비
config 패키지 및 appconfig 클래스 준비
Application Context 수정
---
주석
애노테이션 정의 - > 적용 - > 추출
적용: 클래스 선언, 필드선언, 메서드 선언, 파라미터 선언, 로컬변수 선언에 적용 가능
추출: java Reflection API를 사용하여 추출
* 애노테이션 활용 - 소스코드 자동생성
* 애노테이션 활용 - 컴파일러에게 요청
ex) @Override
* 애노테이션 활용 - 실행 중(Runtime)에 정보 추출
배열;
프로젝트
util - ComponentScan annotation 추가하기
Hashset은 중복되는거 자동제거임. array list는 아님
1. annotation 문법알기
2. 패키지 이름을 알기만 하면 그 안에 클래스들이 무엇인지 알아낼수있다.
public class ApplicationContext {
// 객체 보관소
Map<String,Object> beanContainer = new HashMap<>();
public ApplicationContext(Class<?> configClass) throws Exception {
processBeanAnnotation(configClass);
ComponentScan componentScan = configClass.getAnnotation(ComponentScan.class);
if (componentScan != null) {
processComponentScanAnnotation(componentScan);
}
}
private void processBeanAnnotation(Class<?> configClass) throws Exception {
// 클래스의 기본 생성자를 알아낸다.
Constructor<?> constructor = configClass.getConstructor();
// 기본 생성자를 이용하여 객체를 생성한다.
Object obj = constructor.newInstance();
// 해당 클래스에 정의된 메서드 목록만 가져온다.
Method[] methods = configClass.getDeclaredMethods();
for (Method m : methods) {
// 메서드의 리턴 타입이 없다면 무시한다.
if (m.getReturnType() == void.class) {
continue;
}
// @Bean 애노테이션이 붙지 않은 메서드라면 무시한다.
Bean beanAnnotation = m.getAnnotation(Bean.class);
if (beanAnnotation == null) {
continue;
}
// 메서드 중에서 리턴 값이 있는 메서드를 호출한다.
// 즉 오직 값을 리턴하는 메서드만 호출한다.
Object returnValue = m.invoke(obj);
// 메서드가 리턴한 값을 컨테이너에 저장한다.
if (beanAnnotation.value().length() > 0) {
// 애노테이션에 객체 이름이 지정되어 있다면 그 이름으로 객체를 저장한다.
beanContainer.put(beanAnnotation.value(), returnValue);
} else {
// 애노테이션에 설정된 이름이 없다면 메서드 이름을 사용하여 객체를 저장한다.
beanContainer.put(m.getName(), returnValue);
}
}
}
private void processComponentScanAnnotation(ComponentScan componentScan) throws Exception {
for (String basePackage : componentScan.basePackages()) {
System.out.println(basePackage + "---------------------------------");
createBeans(basePackage);
}
}
private void createBeans(String basePackage) throws Exception {
// 패키지 이름에 해당하는 디렉토리 정보를 알아낸다.
// 1) 패키지 이름을 파일 경로로 변환한다(. --> /)
String packagePath = basePackage.replaceAll("[.]", "/");
// 2) 클래스 경로(CLASSPATH)를 관리하는 객체를 준비한다.
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
// 3) 클래스로더에게 패키지 경로에 해당하는 디렉토리 정보를 읽을 수 있는 도구를 달라고 요구한다.
InputStream dirInputStream = systemClassLoader.getResourceAsStream(packagePath);
if (dirInputStream == null) {
return;
}
// 4) 패키지 디렉토리 안에 들어있는 파일 이름이나 하위 디렉토리 이름을 읽을 도구를 준비한다.
BufferedReader dirReader = new BufferedReader(new InputStreamReader(dirInputStream));
// 5) 디렉토리 리더를 통해 해당 디렉토리에 들어 있는 하위 디렉토리 또는 파일 이름을 알아낸다.
// ==> 전통적인 컬렉션 데이터 가공 방식
// - 한 줄씩 읽으면 된다.
// Set<Class<?>> classes = new HashSet<>();
// String filename = null;
// while ((filename = dirReader.readLine()) != null) {
//
// // 파일 확장자가 .class 로 끝나는 파일만 처리한다.
// if (filename.endsWith(".class")) {
//
// // 패키지 이름과 클래스 이름(.class 확장자를 뺀 이름)을 합쳐서
// // Fully-Qualified class name을 만든 다음에
// // Class.forName() 을 사용하여 클래스를 메모리(Method Area)에 로딩한다.
// Class<?> clazz = Class.forName(basePackage + "." + filename.replace(".class", ""));
//
// // 로딩한 클래스 정보를 Set 컬렉션에 담는다.
// classes.add(clazz);
// }
// }
// ==> 스트림 프로그래밍 기법을 이용하여 컬렉션 데이터를 가공하는 방식
// Set<Class<?>> classes = dirReader
// .lines() // 오리지널 문자 단위 스트림을 줄 단위로 문자열로 나열한 스트림으로 변환한다.
// .filter(new Predicate<String>() { // 스트림에서 처리할 대상을 선택하는 필터를 꼽는다.
// @Override
// public boolean test(String filename) {
// // 이 필터는 파일의 확장자가 .class 로 끝나는 경우에만 처리한다.
// return filename.endsWith(".class");
// }})
// .map(new Function<String, Class<?>>() { // 파일 이름을 받아서 Class 정보를 리턴하는 플러그인 장착한다.
// @Override
// public Class<?> apply(String filename) {
// try {
// return Class.forName(basePackage + "." + filename.replace(".class", ""));
// } catch (Exception e) {
// // 클래스 로딩하다가 예외가 발생하면 무시한다.
// }
// return null;
// }})
// .collect(Collectors.toSet()); // 스트림을 수행하여 최종 결과물은 Set 컬렉션에 담아서 리턴하라고 명령한다.
// ==> 스트림 프로그래밍 방식( + 람다)
Set<Class<?>> classes = dirReader
.lines()
.filter(filename -> filename.endsWith(".class"))
.map(filename -> {
try {
return Class.forName(basePackage + "." + filename.replace(".class", ""));
} catch (Exception e) {
return null;
}
})
.collect(Collectors.toSet());
for (Class<?> clazz : classes) {
System.out.println("##### " + clazz.getName());
}
}
public Object getBean(String name) {
return beanContainer.get(name);
}
public String[] getBeanNames() {
return beanContainer.keySet().toArray(new String[0]);
}
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new ApplicationContext(AppConfig.class);
for (String name : applicationContext.getBeanNames()) {
System.out.println(name);
}
}
}
'[네이버클라우드] 클라우드 기반의 개발자 과정 7기 > 웹프로그래밍' 카테고리의 다른 글
[NC7기-70일차(8월2일)] - 웹프로그래밍 51일차 (0) | 2023.08.02 |
---|---|
[NC7기-69일차(8월1일)] - 웹프로그래밍 50일차 (0) | 2023.08.01 |
[NC7기-67일차(7월28일)] - 웹프로그래밍 48일차 (0) | 2023.07.28 |
[NC7기-66일차(7월27일)] - 웹프로그래밍 47일차 (0) | 2023.07.27 |
[NC7기-65일차(7월26일)] - 웹프로그래밍 46일차 (0) | 2023.07.26 |