인터페이스 활용

허성재's avatar
Aug 21, 2024
인터페이스 활용
인터페이스는 객체지향 프로그래밍에서 매우 중요한 개념입니다. 이를 활용하면 코드의 유연성과 확장성을 높일 수 있습니다. 아래에서는 인터페이스를 어떻게 활용할 수 있는지에 대한 다양한 예제와 개념을 설명해 드리겠습니다.

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 인터페이스를 통해 DogCat 클래스가 각각의 방식으로 makeSound() 메서드를 구현했습니다. 인터페이스를 사용하여 DogCat을 동일한 타입인 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 인터페이스를 사용하여 CreditCardProcessorPayPalProcessor 클래스가 각각의 결제 방식을 구현했습니다. 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 인터페이스는 정렬 알고리즘을 정의하고 있습니다. BubbleSortQuickSort 클래스는 각각의 정렬 방식을 구현합니다. 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

heo-gom