Problema di progettazione con l'iniezione di deleghe, ereditarietà e dipendenza

0

La mia domanda riguarda l'utilizzo della delega insieme all'iniezione di ereditarietà e dipendenza.

Ho una classe MailerService che richiede un delegato per fare il suo lavoro.

Inoltre, ho una gerarchia di delegati con un'astrazione: MailerServiceDelegate insieme a un numero di implementazioni, tra cui un EmailResetDelegate .

Ecco la classe Spring che utilizza il delegato:

@Service
public class MailerService {

    private static final boolean IS_HTML = true;
    private static final boolean MULTIPART = true;
    private static final String ENCODING = "UTF-8";

    ... 
    private final JavaMailSender mailSender;
    private MailerServiceDelegate mailerServiceDelegate;

    public MailerService(...
                         JavaMailSender mailSender,
                         ...) {
        this.mailSender = mailSender;
    }

    public void setMailerServiceDelegate(MailerServiceDelegate mailerServiceDelegate) {
        this.mailerServiceDelegate = mailerServiceDelegate;
    }

    public void sendMail(Object ...objects) {
        try {
            final Context ctx = new Context();
            ...
            mailerServiceDelegate.setContext(ctx, objects);
            final MimeMessage mimeMessage = mailSender.createMimeMessage();
            ... 
            mailSender.send(mimeMessage);
        } catch (MessagingException | UnsupportedEncodingException e) {
            throw new MailerException(e);
        }
    }
}

Ecco l'implementazione per il delegato:

//As of now this is a plain class and not a spring dependency
public class EmailResetDelegate implements MailerServiceDelegate {

E il cliente:

@Service
public class Client {

    private MailerService mailerService;

    public Client(MailerService mailerService) {
        this.mailerService = mailerService;
    }

    public void sendEmailAddressResetRequest(UserAccount userAccount, EmailReset emailReset) {
        mailerService.setMailerServiceDelegate(new EmailResetDelegate());
        mailerService.sendMail(userAccount, emailReset);
    }
}

Il problema con il design attuale è che devo impostare manualmente il delegato usando il metodo setMailerServiceDelegate dal client. Inoltre sto mescolando l'iniezione di dipendenza con l'istanziazione manuale. Qualcuno può suggerire un design migliore?

Modifica : questo è il diagramma per il progetto corrente:

    
posta balteo 05.10.2018 - 08:53
fonte

2 risposte

1

Sembra che MailService debba avere solo un MailerServiceDelegate durante la sua vita. Non sono sicuro che tu possa modificare MailService, ma penso che l'attuale implementazione abbia alcuni difetti.

Se non riesci a cambiare MailService, l'opzione migliore è utilizzare il tuo adattatore su MailService che conterrà più istanze di MailService, per ogni MailServiceDelegate:

public class MailServiceAdapter {

    MailService _emailResetService;
    MailService _otherMailService;

    public MailServiceAdapter(MailServiceFactory mailServiceFactory){
        _emailResetService = mailServiceFactory.CreateMailResetService();
        _otherMailService = mailServiceFactory.CreateOtherMailService();
    }
    public void sendEmailAddressResetRequest(UserAccount userAccount, EmailReset emailReset)
    {
         _emailResetService.sendEmail(userAccount, emailReset);
    }

    public void sendOtherStuff(UserAccount userAccount, OtherEmail otherEMail)
    {
         _otherMailService.sendEmail(userAccount, otherEMail);
    }
}
    
risposta data 04.11.2018 - 20:33
fonte
0

Poiché si tratta di un servizio di Spring, presumo che l'istanza di MailerService sia un singleton.

Fare questo mailerService.setMailerServiceDelegate(new EmailResetDelegate()); in un'applicazione reale significa che prima o poi entrerai in una condizione di competizione in cui due thread differenti non vogliono usare lo stesso delegato.

Quindi, dato che la tua classe è singleton, il suo attributo deve essere definitivo e non modificabile, almeno una volta passato un ulteriore metodo post-init di Spring.

Se vuoi veramente mantenere la tua logica di delega, usa una fabbrica come intermediario tra il tuo servizio e i tuoi delegati.

Tale factory restituirà l'istanza delegato corretta in base a un parametro che gli verrà passato.

Tieni presente che, poiché hai un metodo setContext nella tua posta, ciò significa che la fabbrica dovrà restituire una nuova istanza ogni volta che viene chiamata. Se si desidera utilizzare un singleton, modificare il progetto in modo che il delegato non cambierà una volta inizializzato correttamente.

    
risposta data 05.10.2018 - 14:01
fonte