class Car{}
class SportsCar extends Car{}
class Truck extends Car{}
위와 같은 클래스들의 상속관계가 있다고 할 때
SportsCar car = new SportsCar();
Truck car = new Truck();
위 코드를
Car car = new SportsCar();
Car car = new Truck();
다형성을 이용하면 다음과 같이 바꿀 수 있고 객체를 SportsCar에서 Truck으로 변경하게 될 경우, 다형성을 사용하지 않은 코드에서는 참조변수타입과 객체, 2부분을 바꾸어야 되는 반면, 다형성을 사용하는 코드는 객체부분만 바꾸면 된다는 점에서 변경포인트가 하나 줄게되어 변경에 용이하다. 이를 더 변경에 용이하게 바꾸면 다음과 같다.
Car car = getCar();
static Car getCar() {
return new SportsCar();
}
이와 같이 getCar 메서드를 만들고 Car타입의 변수에 SportsCar 객체의 내용을 넣을 수도 있다. 이렇게하면 SportsCar가 아닌 Truck의 객체를 생성하여도 getCar메서드의 내용만 바꾼다면 getCar를 무수히 많이 호출하여도 모두 변경에 용이한 점이 있다.
Car car = getCar();
static Car getCar() throws Exception {
Properties p = new Properties(); //config.txt를 읽어서 Properties에 저장
p.load(new FileReader("config.txt"));
Class clazz = Class.forName(p.getProperty("car")); //클래스 객체를 얻어서
return (Car)clazz.newInstance(); //객체를 생성해서 반환
위 코드에서 Properties객체를 생성한다. Properties객체는 Map과 같이 key, value로 되어있고 모두 String형을 받는다.
[config.txt]
car=com.fastcampus.ch3.SportsCar
위 txt파일에서 car는 key, com.fastcampus.ch3.SportsCar는 value이다. Properties객체에서 이를 읽어 key, value로 저장한다. SportsCar가 아닌 Truck 클래스의 내용을 사용하고 싶으면 txt파일만 바꾸어주면 된다. 이는 본 코드는 전혀 바꾸지 않고, txt파일만 변화함으로써 프로그램을 변경하지 않아도 된다는 큰 장점이 있다.
위를 더 변경에 유리하게 바꿀 수 있다.
Car car = (Car)getObject("car");
Engine engine = (Engine)getObject("engine");
static Object getObject(String key) throws Exception {
//config.txt를 읽어서 Properties에 저장
Properties p = new Properties();
p.load(new FileReader("config.txt"));
//클래스 객체를 얻어서
Class clazz = Class.forName(p.getProperty(key));
return clazz.newInstance(); //객체를 생성해서 반환
[config.txt]
car=com.fastcampus.ch3.SportsCar
engine=com.fastcampus.ch3.Engine
위 코드에서는 car, engine을 key, value형태로 p에 저장한다. Object형을 써서 Car, Engine형 둘다 다룰수 있다.
class AppContext {
Map map; //객체 저장소
AppContext() { //초기화
map = new HashMap();
map.put("car", new SportsCar());
map.put("engine", new Engine());
}
Object getBean(String id) {return map.get(id); }
}
AppContext ac = new AppContext(); //ac객체 생성
Car car = (Car)ac.getBean("car"); //key값 주기
Engine engine = (Engine)ac.getBean("engine");
@Component class Car {}
@Component class SportsCar extends Car{}
@Component class Truck extends Car{}
@Component class Engine{}
ClassLoader classloader = AppContext.class.getClassLoader();
ClassPath classPath = ClassPath.from(classloader);
//패키지 내의 모든 클래스를 읽어서 Set에 저장
Set<ClassPath.ClassInfo> set = classPath.getTopLevelClasses("com.fastcampus.ch3");
for(ClassPath.ClassInfo classInfo : set) { //패키지 내에 @Component붙은 클래스 찾기
Class clazz = classInfo.load();
Component component = (Component)clazz.getAnnotation(Component.class);
if(component!=null) {
String id = StringUtils.uncapitalize(classInfo.getSimpleName());
map.put(id, clazz.newInstance()); //객체 생성해서 map에 저장
}
}
@Component가 붙어있는 클래스를 순회. 붙어있지 않으면 순회안함.
AppContext ac = new AppContext();
Car car = (Car)ac.getbean("car"); //이름(id)으로 찾기
Car car2 = (Car)ac.getbean(Car.class); //타입으로 찾기
이름으로 찾으면 key값에서 일치하는 값을 찾는것이고, 타입으로 찾으면 value에서 일치하는 값을 찾는것이다.
Object getBean(String id) {
return map.get(id);
}
--이름으로 찾기--
Object getBean(Class clazz) {
for(Object obj: map.values()) {
if(clazz.isInstance(obj)) // obj instanceof clazz를 이용해서 타입 일치확인
return obj;
}
return null;
}
--타입으로 찾기--
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car");
Engine engine = (Engine)ac.getBean("engine");
Door door = (Door)ac.getBean("door");
car.engine = engine;
car.door = door;
class Car {
Engine engine;
Door door;
}
class Car {
@Autowired Engine engine;
@Autowired Door door;
}
@Autowired를 붙이면 맵에 저장되있는 값을 찾아 자동연결을 해준다.
(타입으로 찾는다)
class Engine{}
@Component class SuperEngine extends Engine {}
@Component class TurboEngine extends Engine {}
@Component
class Car {
@Value("red") String color;
@Value("100") int oil;
@Autowired
@Qualifier("superEngine")
Engine engine;
@Autowired에서 검색한 타입이 여러개일 경우, 일치하는 것으로 찾는다. 하지만 위 코드와 같이 @Autowired에서 검색한 타입이 2개인데 둘 다 일치하지 않는다면 @Qualifier를 사용해 직접 지정해주어야 한다.
AppContext ac = new AppContext();
Car car = (Car)ac.getBean("car");
Engine engine = (Engine)ac.getBean("engine");
Door door = (Door)ac.getBean("door");
car.engine = engine;
car.door = door;
class Car{
Engine engine;
Door door;
}
class Car {
@Resource Engine engine; //Engine의 첫글자 E를 소문자로 바꾼것이 이름으로 사용됨(name = "engine")생략
@Resource Door door;
}
@Resource를 붙이면 맵에 저장되어있는 값을 찾아 자동연결을 해준다.
(이름으로 찾는다)
[전통적인 방식] 사용자 코드가 Framework 코드를 호출
Car car = new Car();
car.turboDrive(); // 호출
void turboDrive() {
engine = new TurboEngine();
engine.start();
[IoC] Framework 코드가 사용자 코드를 호출
Car car = new Car();
car.drive(new SuperEngine()); //사용자 코드
void drive(Engine engine) {
engine.start(); //호출
}
호출 방향이 반대.
위 코드의 전통적인 방식에서 turboDrive 메서드는 잘 변하지 않는 코드인데반해, TurboEngine 메서드는 잘변하는 코드이다. IoC방식은 자주 변하는 SuperEngine() 메서드를 빼냄으로써 drive 메서드가 자주 변하지 않는 메서드로 바꾸어주는것이다.