Po to aby klasa menu nie była zależna bezpośrednio od klas serwisów, chciałem zrobić coś w tym stylu:
Podana przez Ciebie klasa w dalszym ciągu jest bezpośrednio zależna od przytoczonych serwisów - fakt, że tworzysz instancje na własną rękę nic tu nie zmienia :-)
Powinno się postawić w tym miejscu pytanie: czy zależności są złe?, prowadzące do mojej skromnej odpowiedzi: zależności nie tylko nie są złe - są naturalne!
Menu reprezentuje akcje, które mogą zostać wykonane przez użytkownika, więc siłą rzeczy musi być zależne od tych serwisów. Tworzenie setek warstw abstrakcji, unikanie DI itd. jedynie utrudnia analizę kodu, nie wnosząc przy tym żadnej faktycznej wartości.
Jeśli chcesz zrobić to porządnie i nie przesadzić z abstrakcjami, powinieneś IMO podejść w taki sposób:
// Interfejs opisujący kontrakt serwisu odpowiedzialnego za rozsyłanie e-maili
interface EmailSender {
void sendEmail(...);
}
// Przykładowa implementacja rozsyłacza e-maili, zapisująca e-maile do logów
class DummyEmailSender implements EmailSender {
/* ... */
}
// Przykładowa implementacja rozsyłacza e-maili, oparta o hipotetycznego faktycznego klienta SMTP
class SmtpEmailSender implements EmailSender {
/* ... */
}
// Interfejs opisujący kontrakt serwisu odpowiedzialnego za rozsyłanie SMSów
interface SmsSender {
void sendSms(...);
}
// Przykładowa implementacja rozsyłacza SMSów, zapisująca SMSy do logów
class DummySmsSender implements SmsSender {
/* ... */
}
// Przykładowa implementacja rozsyłacza SMSów oparta o Twilio
class TwilioSmsSender implements SmsSender {
/* ... */
}
class SomeMenu {
private EmailSender emailSender; // <- opieramy się na interfejsach, dzięki czemu możemy tutaj podać zarówno faktyczną implementację, jak i naszą testową ("dummy")
private SmsSender smsSender;
public constructor(EmailSender emailSender, SmsSender smsSender) {
/* ... */
}
/* ... */
}
Zauważ, że zależność pozostała (od interfejsu), lecz dostaliśmy w zamian dowolność w dobieraniu implementacji - możemy sobie zrobić new SomeMenu(new DummyEmailSender(), new DummySmsSender())
i boom, mamy menu, które działa tak jak prawdziwe, a jednocześnie nie rozsyła żadnych faktycznych maili oraz SMSów.