Il y a beaucoup de bonnes réponses ici, mais je trouve souvent que l'utilisation des DEUX interfaces et des classes abstraites est la meilleure voie. Considérez cet exemple artificiel:
Vous êtes développeur de logiciels dans une banque d'investissement et devez créer un système qui place des ordres sur un marché. Interface capture de ce qu'est un système commercial plus idée générale fait ,
1) Trading system places orders
2) Trading system receives acknowledgements
et peut être capturé dans une interface, ITradeSystem
public interface ITradeSystem{
public void placeOrder(IOrder order);
public void ackOrder(IOrder order);
}
Désormais, les ingénieurs travaillant au bureau des ventes et dans d'autres secteurs d'activité peuvent commencer à s'interfacer avec votre système pour ajouter des fonctionnalités de placement de commande à leurs applications existantes. Et vous n'avez même pas encore commencé à construire! C'est la puissance des interfaces.
Alors vous allez de l'avant et construisez le système pour les négociants en actions ; ils ont entendu dire que votre système a une fonction pour trouver des actions bon marché et sont très impatients de l'essayer! Vous capturez ce comportement dans une méthode appelée findGoodDeals()
, mais vous réalisez également qu'il y a beaucoup de choses compliquées qui sont impliquées dans la connexion aux marchés. Par exemple, vous devez ouvrir un SocketChannel
,
public class StockTradeSystem implements ITradeSystem{
@Override
public void placeOrder(IOrder order);
getMarket().place(order);
@Override
public void ackOrder(IOrder order);
System.out.println("Order received" + order);
private void connectToMarket();
SocketChannel sock = Socket.open();
sock.bind(marketAddress);
<LOTS MORE MESSY CODE>
}
public void findGoodDeals();
deals = <apply magic wizardry>
System.out.println("The best stocks to buy are: " + deals);
}
Les implémentations concrètes vont avoir beaucoup de ces méthodes désordonnées connectToMarket()
, mais findGoodDeals()
c'est tout ce dont les traders se soucient réellement.
Voici maintenant où les classes abstraites entrent en jeu. Votre patron vous informe que les traders de devises souhaitent également utiliser votre système. Et en regardant les marchés des devises, vous voyez que la plomberie est presque identique aux marchés boursiers. En fait, connectToMarket()
peut être réutilisé textuellement pour se connecter aux marchés des changes. Cependant, findGoodDeals()
c'est un concept très différent dans l'arène monétaire. Donc, avant de transmettre la base de code à l'enfant sorcier des changes de l'autre côté de l'océan, vous commencez par refactoriser en une abstract
classe, laissant non findGoodDeals()
implémenté
public abstract class ABCTradeSystem implements ITradeSystem{
public abstract void findGoodDeals();
@Override
public void placeOrder(IOrder order);
getMarket().place(order);
@Override
public void ackOrder(IOrder order);
System.out.println("Order received" + order);
private void connectToMarket();
SocketChannel sock = Socket.open();
sock.bind(marketAddress);
<LOTS MORE MESSY CODE>
}
Votre système boursier met findGoodDeals()
en œuvre comme vous l'avez déjà défini,
public class StockTradeSystem extends ABCTradeSystem{
public void findGoodDeals();
deals = <apply magic wizardry>
System.out.println("The best stocks to buy are: " + deals);
}
mais maintenant, la gamine fétiche FX peut construire son système en fournissant simplement une implémentation de findGoodDeals()
pour les devises; elle n'a pas à réimplémenter les connexions socket ou même les méthodes d'interface!
public class CurrencyTradeSystem extends ABCTradeSystem{
public void findGoodDeals();
ccys = <Genius stuff to find undervalued currencies>
System.out.println("The best FX spot rates are: " + ccys);
}
La programmation sur une interface est puissante, mais des applications similaires réimplémentent souvent des méthodes de manière presque identique. L'utilisation d'une classe abstraite évite les réimplémentations, tout en préservant la puissance de l'interface.
Remarque: on peut se demander pourquoi findGreatDeals()
ne fait pas partie de l'interface. N'oubliez pas que l'interface définit les composants les plus généraux d'un système commercial. Un autre ingénieur peut développer un système commercial COMPLÈTEMENT DIFFÉRENT, où il ne se soucie pas de trouver de bonnes affaires. L'interface garantit que le bureau de vente peut également s'interfacer avec son système, il est donc préférable de ne pas enchevêtrer votre interface avec des concepts d'application tels que «bonnes affaires».