본문 바로가기
Mobile/Android (Java)

Dependency Injector

by hirudev 2023. 3. 13.

※ 개인적인 메모 같은 느낌으로 설명이 적습니다.

 

Velocity 플러그인 개발 시작 문서를 읽어보고 있는데 눈에 띄는 코드가 있었다.

 

@Inject

 

이를 검색해보니 Google Guice 를 이용하여 Dependency Injection 를 효율적으로 사용할 수 있게끔하는 프레임워크인 듯하다.

 

Dependency injection 을 간단하게 요약하면 factory 패턴과 매우 흡사해 보인다.

 

구성요소로 크게 3가지가 있다.

- Services and clients (모듈 같은 개념)

- Interface

- Injectors

 

자세한 내용은 https://en.wikipedia.org/wiki/Dependency_injection 을 읽어보면 될 듯하고... C# 코드는 아래와 같다.

(코드 또한 위 링크에 있다.)

using System;

namespace DependencyInjection;

// Our client will only know about this interface, not which specific gamepad it is using.
interface IGamepadFunctionality {
    string GetGamepadName();
    void SetVibrationPower(float InPower);
}

// The following services provide concrete implementations of the above interface.

class XBoxGamepad : IGamepadFunctionality {
    float VibrationPower = 1.0f;
    
    public string GetGamepadName() => "Xbox controller";
    
    public void SetVibrationPower(float InPower) => VibrationPower = Math.Clamp(InPower, 0.0f, 1.0f);
}

class PlaystationJoystick : IGamepadFunctionality {
    float VibratingPower = 100.0f;
    
    public string GetGamepadName() => "PlayStation controller";
    
    public void SetVibrationPower(float InPower) => VibratingPower = Math.Clamp(InPower * 100.0f, 0.0f, 100.0f);
}

class SteamController : IGamepadFunctionality {
    double Vibrating = 1.0;
    
    public string GetGamepadName() => "Steam controller";
    
    public void SetVibrationPower(float InPower) => Vibrating = Convert.ToDouble(Math.Clamp(InPower, 0.0f, 1.0f));
}

// This class is the client which receives a service.
class Gamepad {
    IGamepadFunctionality _GamepadFunctionality;

    // The service is injected through the constructor and stored in the above field.
    public Gamepad(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

    public void Showcase() {
        // The injected service is used.
        var gamepadName = _GamepadFunctionality.GetGamepadName();
        var message = $"We're using the {gamepadName} right now, do you want to change the vibrating power?";
        Console.WriteLine(message);
    }
}

class Program {
    static void Main() {
        var steamController = new SteamController();
        
        // We could have also passed in an XboxController, PlaystationJoystick, etc.
        // The gamepad doesn't know what it's using and doesn't need to.
        var gamepad = new Gamepad(steamController);
        
        gamepad.Showcase();
    }
}

 

대충 이런 느낌이다.

(팩토리 패턴이랑 똑같긴한데 차이가 있다면 클래스들을 모듈화시켰다는 점에 있다.)

 

이 개념으로 https://github.com/google/guice/wiki/Motivation 을 읽어보면 될 듯하다.

 

아래 셈플 코드는 위 깃허브 링크에 있는거를 가져왔다. (나중에 내가 참고하기 위해...)

public interface BillingService {
  Receipt chargeOrder(PizzaOrder order, CreditCard creditCard);
}
/*───────────────────────────────────────────────────────────────────────*/
public class RealBillingService implements BillingService {
  private final CreditCardProcessor processor;
  private final TransactionLog transactionLog;

  @Inject
  public RealBillingService(CreditCardProcessor processor,
      TransactionLog transactionLog) {
    this.processor = processor;
    this.transactionLog = transactionLog;
  }

  public Receipt chargeOrder(PizzaOrder order, CreditCard creditCard) {
    try {
      ChargeResult result = processor.charge(creditCard, order.getAmount());
      transactionLog.logChargeResult(result);

      return result.wasSuccessful()
          ? Receipt.forSuccessfulCharge(order.getAmount())
          : Receipt.forDeclinedCharge(result.getDeclineMessage());
     } catch (UnreachableException e) {
      transactionLog.logConnectException(e);
      return Receipt.forSystemFailure(e.getMessage());
    }
  }
}
/*───────────────────────────────────────────────────────────────────────*/
public class BillingModule extends AbstractModule {
  @Override
  protected void configure() {
    bind(TransactionLog.class).to(DatabaseTransactionLog.class);
    bind(CreditCardProcessor.class).to(PaypalCreditCardProcessor.class);
    bind(BillingService.class).to(RealBillingService.class);
  }
}
/*───────────────────────────────────────────────────────────────────────*/
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BillingModule());
    BillingService billingService = injector.getInstance(BillingService.class);
    ...
  }

 

그런데 결국 C#에서 적용하려면 mvvm 바인딩처럼 인스턴스를 생성해주고 각 모듈을 바인딩해줄 프레임워크가 필요해보인다. (있나? 찾아보니 있다 https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection )

 

댓글