인터페이스는 객체지향 프로그래밍에서 매우 중요한 개념입니다. 이를 활용하면 코드의 유연성과 확장성을 높일 수 있습니다. 아래에서는 인터페이스를 어떻게 활용할 수 있는지에 대한 다양한 예제와 개념을 설명해 드리겠습니다.
1. 다형성 (Polymorphism)
인터페이스는 다형성을 구현하는 강력한 도구입니다. 다형성은 같은 인터페이스를 구현하는 여러 객체가 동일한 메서드를 다른 방식으로 구현할 수 있게 해줍니다.
예제: 동물 소리
interface Animal {
void makeSound();
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("Woof");
}
}
class Cat implements Animal {
@Override
public void makeSound() {
System.out.println("Meow");
}
}
public class Main {
public static void main(String[] args) {
Animal myDog = new Dog();
Animal myCat = new Cat();
myDog.makeSound(); // Woof
myCat.makeSound(); // Meow
}
}
- 이 예제에서
Animal
인터페이스를 통해Dog
와Cat
클래스가 각각의 방식으로makeSound()
메서드를 구현했습니다. 인터페이스를 사용하여Dog
와Cat
을 동일한 타입인Animal
로 다룰 수 있으며, 각 클래스의 구체적인 구현은 런타임에 결정됩니다.
2. 코드의 느슨한 결합 (Loose Coupling)
인터페이스를 사용하면 코드의 결합도를 낮출 수 있습니다. 이는 시스템의 특정 부분을 쉽게 변경하거나 확장할 수 있게 합니다.
예제: 결제 시스템
interface PaymentProcessor {
void processPayment(double amount);
}
class CreditCardProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card payment of $" + amount);
}
}
class PayPalProcessor implements PaymentProcessor {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of $" + amount);
}
}
class CheckoutService {
private PaymentProcessor processor;
public CheckoutService(PaymentProcessor processor) {
this.processor = processor;
}
public void checkout(double amount) {
processor.processPayment(amount);
}
}
public class Main {
public static void main(String[] args) {
PaymentProcessor creditCard = new CreditCardProcessor();
PaymentProcessor paypal = new PayPalProcessor();
CheckoutService service1 = new CheckoutService(creditCard);
service1.checkout(100.0); // Processing credit card payment of $100.0
CheckoutService service2 = new CheckoutService(paypal);
service2.checkout(200.0); // Processing PayPal payment of $200.0
}
}
- 이 예제에서
PaymentProcessor
인터페이스를 사용하여CreditCardProcessor
와PayPalProcessor
클래스가 각각의 결제 방식을 구현했습니다.CheckoutService
는 결제 방식을 알고 있을 필요 없이PaymentProcessor
인터페이스만 의존합니다. 결제 방식이 추가되더라도CheckoutService
는 변경할 필요가 없습니다.
3. 전략 패턴 (Strategy Pattern)
인터페이스는 전략 패턴을 구현하는 데 자주 사용됩니다. 전략 패턴은 알고리즘을 캡슐화하여 동적으로 교체할 수 있게 해줍니다.
예제: 정렬 전략
interface SortingStrategy {
void sort(int[] numbers);
}
class BubbleSort implements SortingStrategy {
@Override
public void sort(int[] numbers) {
System.out.println("Sorting array using bubble sort");
// Bubble sort algorithm...
}
}
class QuickSort implements SortingStrategy {
@Override
public void sort(int[] numbers) {
System.out.println("Sorting array using quick sort");
// Quick sort algorithm...
}
}
class Sorter {
private SortingStrategy strategy;
public Sorter(SortingStrategy strategy) {
this.strategy = strategy;
}
public void sort(int[] numbers) {
strategy.sort(numbers);
}
}
public class Main {
public static void main(String[] args) {
int[] numbers = {5, 3, 8, 1};
Sorter bubbleSorter = new Sorter(new BubbleSort());
bubbleSorter.sort(numbers); // Sorting array using bubble sort
Sorter quickSorter = new Sorter(new QuickSort());
quickSorter.sort(numbers); // Sorting array using quick sort
}
}
- 여기서
SortingStrategy
인터페이스는 정렬 알고리즘을 정의하고 있습니다.BubbleSort
와QuickSort
클래스는 각각의 정렬 방식을 구현합니다.Sorter
클래스는SortingStrategy
인터페이스에 의존하며, 정렬 알고리즘을 유연하게 교체할 수 있습니다.
4. 템플릿 메서드 패턴 (Template Method Pattern)과 함께 사용
인터페이스는 템플릿 메서드 패턴에서 훅 메서드를 정의하는 데 사용될 수 있습니다. 이 패턴은 알고리즘의 골격을 정의하고, 일부 단계를 서브클래스에서 재정의할 수 있게 합니다.
예제: 요리 템플릿
interface Cooking {
void prepareIngredients();
void cook();
void serve();
}
abstract class CookingTemplate implements Cooking {
// 템플릿 메서드
public final void prepareMeal() {
prepareIngredients();
cook();
serve();
}
@Override
public abstract void prepareIngredients();
@Override
public abstract void cook();
@Override
public abstract void serve();
}
class PastaCooking extends CookingTemplate {
@Override
public void prepareIngredients() {
System.out.println("Preparing pasta and sauce ingredients");
}
@Override
public void cook() {
System.out.println("Cooking pasta and heating sauce");
}
@Override
public void serve() {
System.out.println("Serving pasta with sauce");
}
}
class SteakCooking extends CookingTemplate {
@Override
public void prepareIngredients() {
System.out.println("Preparing steak and seasoning");
}
@Override
public void cook() {
System.out.println("Grilling steak");
}
@Override
public void serve() {
System.out.println("Serving steak with vegetables");
}
}
public class Main {
public static void main(String[] args) {
CookingTemplate pastaCooking = new PastaCooking();
pastaCooking.prepareMeal();
CookingTemplate steakCooking = new SteakCooking();
steakCooking.prepareMeal();
}
}
- 이 예제에서
CookingTemplate
추상 클래스는 요리의 골격을 정의하고, 각 요리 단계는Cooking
인터페이스를 구현하여 서브클래스에서 구체적으로 구현됩니다. 이 방법은 템플릿 메서드 패턴과 인터페이스를 결합하여 유연성을 높이는 데 사용됩니다.
5. 디자인 패턴과의 결합
인터페이스는 다양한 디자인 패턴에서 중요한 역할을 합니다. 전략 패턴, 템플릿 메서드 패턴 외에도 팩토리 패턴, 데코레이터 패턴 등에서도 인터페이스는 핵심적으로 사용됩니다.
결론
인터페이스는 객체지향 프로그래밍에서 코드를 더 유연하고 확장 가능하게 만드는 중요한 도구입니다. 인터페이스를 활용하면 다형성, 느슨한 결합, 전략 패턴 등 다양한 설계 패턴을 구현할 수 있으며, 이는 결국 코드의 유지보수성과 재사용성을 높이는 데 크게 기여합니다.
이제 인터페이스를 어떻게 활용할 수 있는지, 그리고 왜 중요한지 더 명확하게 이해할 수 있을 것입니다. 추가적으로 궁금한 부분이나 다른 예제가 필요하면 언제든지 질문해 주세요!
Share article