라이브러리 vs 프레임워크의 차이
라이브러리
- 공통으로 사용될 수 있는 특정한 기능을 모듈화한 것
- 내가 직접 컨트롤할 수 있음.
- 프레임워크에 비해 자유로움.
프레임워크
- 공통으로 사용될 수 있는 특정한 기능을 모듈화한 것.
- 규칙이 엄격함.(폴더 구조, 파일 명 등)
- 제어권이 나한테 없음.(IOC)
디자인 패턴
- 프로그램을 설계할 때, 발생했던 문제점들을 객체 간의 상호 관계 등을 이용하여 해결할 수 있도록 하나의 "규약" 형태로 만들어 놓은 것.
싱글톤 패턴
- 하나의 클래스에 오직 하나의 인스턴스만 가지는 패턴
- 데이터베이스 연결 모듈에 많이 쓰인다.
- 인스턴스를 생성할 때, 드는 비용이 줄어든다.
- 의존성이 높아진다.
중첩 클래스란?
- 클래스 내부에 클래스가 있는 형태
- 자바 기반의 UI 처리를 할 때, 사용자의 입력이나 외부의 이벤트에 대한 처리르 하는 곳에서 많이 사용했으나, 현재는 안드로이드 프로그래밍에서 위젯의 이벤트를 처리하는 핸들러를 구현할 때, 사용한다.
- 다른 클래스와 협력할 일이 적고, 외부 클래스와 밀접한 관련이 있다면 사용된다. -> 두 클래스를 한 번에 관리하기에 적합하고, 불필요한 관계를 감추고 높은 접근성을 얻을 수 있다.
- 외부 클래스와 내부 클래스가 서로의 멤버에 쉽게 접근할 수 있음.
- 소스의 가독성과 유지보수성을 높일 수 있다.
- 서로 관련 있는 클래스를 논리적으로 묶어서 표현함으로써, 코드의 캡슐화를 증가시킨다.
- 외부에서는 내부 클래스에 접근할 수 없으므로, 코드의 복잡성을 줄일 수 있다.
참고: https://velog.io/@blwasc/Java-%EC%A4%91%EC%B2%A9-%ED%81%B4%EB%9E%98%EC%8A%A4
[Java] 중첩 클래스
중첩 클래스(Nested class)란?, 중첩 클래스를 사용하는 이유, 중첩 클래스의 장점, 중첩 클래스의 종류, 내부 인터페이스(Nested Interface)란?
velog.io
* 중첩 클래스를 이용해서 활용한 예시
class Singleton {
private static class singleInstanceHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return singleInstanceHolder.INSTANCE;
}
}
public class HelloWorld {
public static void main(String[] args) {
Singleton a = Singleton.getInstance();
Singleton b = Singleton.getInstance();
System.out.println(a.hashCode());
System.out.println(b.hashCode());
if (a == b) {
System.out.println(true);
}
}
}
싱글톤 패턴의 단점
1. '독립적인' 인스턴스를 만들기가 어려움 -> 단위 테스트는 테스트가 서로 독립적이어야 하는데, 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로
단위테스트가 '독립적' -> 테스트를 어떤 순서로든 실행할 수 있어야 한다.
2. 모듈 간의 결합을 강하게 만들 수 있다. => 의존성 주입(DI)를 통해 결합도를 낮출 수 있음.
의존성
- 종속성이라고도 불림.
- A가 B에 의존성이 있다는 것은 B의 변경 사항에 대해 A 또한 변해야 한다는 것.
의존성 주입 장점
- 모듈들을 쉽게 교체할 수 있는 구조로 만들 수 있다. -> 메인 모듈이 '직접' 다른 하위 모듈에 대한 의존성을 주기보다는 중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 '간접적'으로 의존성 주입을 주는 것.
- 테스팅하기 쉽고, 마이그레이션하기도 수월함.
구현할 때, 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어 주기 때문에 애플리케이션 의존성 방향이 일관되고, 애플리케이션을 쉽게 추론할 수 있고, 모듈 간의 관계가 명확해짐.
의존성 주입 단점
- 모듈이 더 많이 분리되므로, 클래스 수가 많아지고 이로 인해, 약간의 런타임 패널티가 발생할 수 있음.
의존성 주입 원칙
"상위 모듈은 하위 모듈에서 어떠한 것도 가져와서는 안된다."
- 상위 모듈과 하위 모듈 모두 "추상화"에 의존해야 한다.
- 추상화는 "세부 사항"에 의존해서는 안된다.
팩토리 패턴
- 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴
- 상속관계가 있는 두 클래스에서 상위 클래스는 크게 뼈대를 결정하고, 하위 클래스는 객체 생성에 관한 구체적인 내용을 결정하는 패턴
- 상위 클래스는 객체 생성 방식에 대해 알 필요가 없고, 느슨한 결합이 가능하다.
- 객체 생성 로직이 따로 분리되어 있기 때문에 수정할 일이 있다면 그 부분만 고치면 되므로 유지보수성이 늘어난다.
- 전달받은 값에 따라 다른 객체를 생성하며 인스턴스의 타입을 정한다.
enum CoffeeType {
LATTE,
ESPRESSO
}
abstract class Coffee {
protected String name;
public String getName() {
return name;
}
}
class Latte extends Coffee {
public Latte() {
name = "Latte";
}
}
class Espresso extends Coffee {
public Espresso() {
name = "Espresso";
}
}
class CoffeeFactory {
public static Coffee createCoffee(CoffeeType type) {
switch (type) {
case LATTE:
return new Latte();
case ESPRESSO:
return new Espresso();
default:
throw new IllegalArgumentException("invalid type");
}
}
}
public class Main {
public static void main(String[] args) {
Coffee coffee = CoffeeFactory.createCoffee(CoffeeType.LATTE);
System.out.println(coffee.getName());
}
}
Enum
- 상수의 집합을 정의할 때, 사용되는 타입
- 상수뿐만 아니라 메서드를 집어 넣어 관리할 수도 있음.
- 본질적으로 thread safe 하기 때문에 싱글톤 패턴을 만들 때 도움이 된다.
전략 패턴
- 객체의 행위를 바꾸고 싶은 경우, "직접" 수정하지 않고 전략이라고 부르는 '캡슐화된 알고리즘'을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴
- 우리가 어떤 상품을 구매할 때, 네이버페이, 카카오페이 등 다양한 방법으로 결제하듯이 결제 방식의 '전략'만 바꿔서 두 가지 방식으로 결제하는 것
interface PaymentStrategy {
public void pay(int amount);
}
class KAKAOCardStrategy implements PaymentStrategy {
private String name;
private String cardNumber;
private String cvv;
private String dateOfExpiry;
public KAKAOCardStrategy(String name, String cardNumber, String cvv, String dateOfExpiry) {
this.name = name;
this.cardNumber = cardNumber;
this.cvv = cvv;
this.dateOfExpiry = dateOfExpiry;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using KAKAOCard.");
}
}
class LUNACardStrategy implements PaymentStrategy {
private String emailId;
private String password;
public LUNACardStrategy(String emailId, String password) {
this.emailId = emailId;
this.password = password;
}
@Override
public void pay(int amount) {
System.out.println(amount + " paid using LUNACard.");
}
}
class Item {
private String name;
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
}
class ShoppingCart {
List<Item> items;
public ShoppingCart() {
this.items = new ArrayList<>();
}
public void addItem(Item item) {
this.items.add(item);
}
public void removeItem(Item item) {
this.items.remove(item);
}
public int calculateTotal() {
int sum = 0;
for (Item item : items) {
sum += item.getPrice();
}
return sum;
}
public void pay(PaymentStrategy paymentMethod) {
int amount = calculateTotal();
paymentMethod.pay(amount);
}
}
public class HelloWorld {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
Item A = new Item("knudolA", 100);
Item B = new Item("knudolB", 300);
cart.addItem(A);
cart.addItem(B);
// pay by LUNACard
cart.pay(new LUNACardStrategy("ex1@example.com", "pukubababo"));
// pay by KAKAOCard
cart.pay(new KAKAOCardStrategy("ex2", "123456", "123", "12/01"));
/**
* 400 paid using LUNACard.
* 400 paid using KAKAOCard.
*/
}
}
컨텍스트
- 어떠한 작업을 완료하는데 필요한 모든 관련 정보를 말한다.
출처: 면접을 위한 전공 CS노트