2017-10-10 6 views
0

Dans un module de rapports, il existe un service de courrier électronique que je voudrais refactoriser afin de pouvoir l'utiliser comme service de messagerie généraliste. En fait, j'ai besoin d'envoyer des courriels aux utilisateurs quand ils veulent réinitialiser leur mot de passe, c'est la principale raison du refactoring.Refactorisation d'une méthode de classe à une interface dans Java

public class EmailService{ 

    public Email buildEmail(ReportRequest reportRequest){ 
    //build email using ReportRequest object here 
    } 

} 

@Builder 
@Getter 
@Setter 
@AllArgsConstructor 
public class Email implements Serializable { 
    private String subject; 
    private String text; 
    private String recipientEmail; 
    private String senderEmail; 

} 

La façon dont j'avais refactor c'est comme ceci:

J'ai créé une interface appelée EmailService qui a une méthode buildEmail(). Je pensais que n'importe quelle classe qui implémente ceci aura une manière différente de construire/construire son email.

public interface EmailService{ 
    public Email buildEmail(); 
} 

public class ReportEmailService implements EmailService{ 
    public Email buildEmail(){} 
} 

public class PasswordEmailService implements EmailService{ 
    public Email buildEmail(){} 
} 

Ma question est maintenant, depuis la construction de l'e-mail utilisera différents objets (par exemple ReportRequest ou un autre objet comme accountInfo), quelle serait la meilleure façon de passer l'objet nécessaire dans le buildEmail()?

Ce que j'ai fait ici était de créer une autre méthode et de créer une variable de classe pour l'objet requis qui sera utilisé dans le buildEmail().

En fait, il ressemble maintenant à ceci:

public class ReportEmailService implements EmailService{ 
    private ReportRequest reportRequest; 

    public void sendEmail(ReportRequest reportRequest){ 
     this.reportRequest = reportRequest; 
     Email email = buildEmail(); 
    } 

    public Email buildEmail(){ 
     #build email now using the report request object. 
    } 
} 

public class PasswordResetEmailService implements EmailService{ 
    private AccountInfo accountInfo; 

    public void sendEmail(AccountInfo accountInfo){ 
     this.accountInfo= accountInfo; 
     Email email = buildEmail(); 
    } 

    public Email buildEmail(){ 
     #build email now using the account info object. 
    } 
} 

Je sens que mon approche est quelque peu maladroite. J'ai peut-être manqué quelque chose de fondamental ici en termes de conception et de refactoring, alors quelle pourrait être la meilleure approche pour refactoriser cela? Ou comment le buildEmail() peut-il avoir accès à l'objet spécifique dont il a besoin lorsqu'il construit l'email?

Répondre

3

Les génériques peuvent vous aider à résoudre ce problème.

Déclarez l'interface comme ceci:

interface EmailService<T> { 
    Email buildEmail(T t); 
} 

et vos réalisations ainsi:

class ReportEmailService<ReportRequest> implements EmailService { 
    Email buildEmail(ReportRequest req) { 
     ... 
    } 
} 

La partie "Génériques" est ce qui est entre les chevrons (<T>), il agit comme un espace réservé pour un type que vous définissez plus tard pour chaque implémentation. Les livres sur la conception guidée par domaine définissent qu'un service est un singleton. Dans la plupart des cas, vous ne devez donc pas créer plusieurs instances du même service.

2

Vous pouvez implémenter plusieurs services de messagerie, ou vous pouvez déléguer à l'argument.

interface EmailService { 
    boolean send(EmailFactory arg) 

    interface EmailFactory { 
     Email buildEmail(); 
    } 
} 

Ensuite, vos classes ReportRequest et AccountInfo peut mettre en œuvre le EmailFactory, ou mieux encore, vous créez une classe d'adaptateur qui sait comment buildEmail pour chaque type ...

class ReportRequestEmailFactory implements EmailFactory { 
    private ReportRequest report; 
    public Email buildEmail() { 
     return ... 
    } 
} 

class AccountInfoEmailFactory implements EmailFactory { 
    private AccountInfo account; 
    public Email buildEmail() { 
     return ... 
    } 
} 

De cette façon, vous mettre en œuvre un seul EmailService qui sait seulement comment envoyer des emails. Et vous implémentez des wrappers/adaptateurs spécifiques pour chaque type de chose que vous souhaitez envoyer en tant qu'e-mail.

Ceci est également facilement étendu pour permettre différents types de courriels pour différentes classes de domaine, comme FullDetailsAccountInfoEmailFactory et SummaryAccountInfoEmailFactory.

points de bonus, peut-être, si vous commencez à utiliser des types standards

class EmailService implements Consumer<Email> { 
    public void accept(Email email) { 
     // TODO: send email 
    } 
} 
class AccountInfoEmailTransformer implements Function<AccountInfo,Email> { 
    public Email apply(AccountInfo t) { 
     // TODO: transform AccountInfo to Email 
     return ... 
    } 
} 

Ensuite, vous pouvez faire des choses comme

EmailService emailer = ... 
AccountInfoEmailFunction transformer = ... 

List<AccountInfo> accounts = ... 

accounts.stream().map(transformer).forEach(emailer);