J'ai à peine besoin de discuter de la raison pour laquelle la réutilisation du code est bénéfique. La réutilisation du code facilite généralement le développement de programmes et réduit les bogues. Une fois qu'un morceau de code est encapsulé et réutilisé, il est nécessaire de vérifier un très petit morceau de code pour assurer l'exactitude du programme. Si vous n'avez qu'à ouvrir et fermer la connexion de la base de données en un seul endroit à travers l'application, il est beaucoup plus facile de s'assurer que la connexion est normale. Mais je suis sûr que vous savez déjà tout cela.
Il existe deux types de codes de réutilisation, que j'appelle des types de réutilisation:
Le premier type est une réutilisation fonctionnelle, qui est le type de réutilisation le plus courant. C'est aussi une sorte de maîtrise pour la plupart des développeurs. Autrement dit, réutilisez un ensemble d'instructions ultérieures pour effectuer une opération.
Le deuxième type est la réutilisation de contexte, c'est-à-dire que différentes fonctions ou codes d'opération sont encapsulées entre les mêmes contextes, et le même contexte est encapsulé que le code de réutilisation (le contexte se réfère ici à une série des mêmes instructions de fonctionnement). Bien qu'il devienne plus populaire dans l'inversion du contrôle, il n'est pas courant. De plus, la réutilisation du contexte n'est pas explicitement décrite, elle n'est donc pas utilisée par le système comme la réutilisation fonctionnelle. J'espère que vous changerez après avoir lu cet article.
Réutilisation de la fonction
La réutilisation fonctionnelle est le type de réutilisation le plus courant. Il s'agit d'une réutilisation d'un ensemble d'instructions qui exécutent une sorte d'opération. Les deux méthodes suivantes consistent à lire les données de la base de données:
Liste publique readAnLusers () {connexion connexion = null; String SQL = "SELECT * FROM Users"; Lister les utilisateurs = new ArrayList (); try {connection = openconnection (); Statement PreadStatement = Connection.PrepareStatement (SQL); ResultTset result = instruction.executequery (); while (result.next ()) {// réutiliser le code utilisateur utilisateur = new user (); user.setName (result.getString ("name")); user.setEmail (result.getString ("e-mail")); users.add (utilisateur); // Code de réutilisation final} result.close (); instruction.close (); retourner les utilisateurs; } catch (sqlexception e) {// ignorer pour l'instant} enfin {// ignorer pour l'instant}} public list readUsersOfStatus (String status) {connexion connection = null; String sql = "SELECT * FROM Users Where Status =?"; Lister les utilisateurs = new ArrayList (); try {connection = openconnection (); Statement PreadStatement = Connection.PrepareStatement (SQL); instruction.SetString (1, statut); ResultTset result = instruction.executequery (); while (result.next ()) {// réutiliser le code utilisateur utilisateur = new user (); user.setName (result.getString ("name")); user.setEmail (result.getString ("e-mail")); users.add (utilisateur); // Code de réutilisation final} result.close (); instruction.close (); retourner les utilisateurs; } catch (sqlexception e) {// ignorer pour l'instant} enfin {// ignorer pour l'instant}}Pour les développeurs expérimentés, il peut être possible de découvrir bientôt le code réutilisable. L'endroit où le "code de réutilisation" est commenté dans le code ci-dessus est le même, donc la réutilisation peut être encapsulée. Ce sont les opérations qui lisent les enregistrements des utilisateurs dans les instances utilisateur. Ces lignes de code peuvent être encapsulées dans leurs propres méthodes, par exemple:
// encapsuler la même opération dans la méthode ReadUser Private User ReadUser (ResultSet Result) lève SQLEXception {user user = new User (); user.setName (result.getString ("name")); user.setEmail (result.getString ("e-mail")); users.add (utilisateur); RETOUR UTILISATEUR; }Maintenant, appelez la méthode ReadUser () dans les deux méthodes ci-dessus (l'exemple suivant ne montre que la première méthode):
Liste publique readAnLusers () {connexion connexion = null; String SQL = "SELECT * FROM Users"; Lister les utilisateurs = new ArrayList (); try {connection = openconnection (); Statement PreadStatement = Connection.PrepareStatement (SQL); ResultTset result = instruction.executequery (); while (result.next ()) {users.add (readUser (result))} result.close (); instruction.close (); retourner les utilisateurs; } catch (sqlexception e) {// ignorer pour l'instant} enfin {// ignorer pour l'instant}}La méthode ReadUser () peut également être cachée dans sa propre classe en utilisant le modificateur privé.
Ce qui précède concerne la réutilisation des fonctions. La réutilisation fonctionnelle est d'encapsuler un ensemble d'instructions qui effectuent des opérations spécifiques à travers des méthodes ou des classes pour atteindre le but de la réutilisation.
Opérations paramétrées
Parfois, vous souhaitez réutiliser un ensemble d'opérations, mais ces opérations ne sont pas exactement les mêmes partout où vous utilisez. Par exemple, les méthodes readAlLusers () et readUsersOfStatus () ouvrent à la fois une connexion, préparent une instruction, l'exécutent et la boucle via l'ensemble de résultats. La seule différence est que ReadUsersOfStatus () nécessite un paramètre pour être réglé sur PreadStatement. Nous pouvons encapsuler toutes les opérations dans une méthode ReadUserList (). Comme indiqué ci-dessous:
Private List ReadUserList (String SQL, String [] Paramètres) {Connection Connection = NULL; Lister les utilisateurs = new ArrayList (); try {connection = openconnection (); Statement PreadStatement = Connection.PrepareStatement (SQL); for (int i = 0; i <paramètres.length; i ++) {instruction.setstring (i, paramètres [i]); } ResultSet result = instruction.executequery (); while (result.next ()) {users.add (readUser (result))} result.close (); instruction.close (); retourner les utilisateurs; } catch (sqlexception e) {// ignorer pour l'instant} enfin {// ignorer pour l'instant}} Maintenant, nous appelons readUserList(...) de readAllUsers() et readUsersOfStatus() et donner différents paramètres de fonctionnement:
public list readAnLusers () {return readUserList ("select * from Users", new String [] {});} public list readUserswithstatus (string status) {return readUserList ("SELECT * FROM Users", new String [] {status});}Je crois que vous pouvez trouver d'autres meilleures façons d'implémenter les fonctions de réutilisation et de les paramétrer pour le faciliter l'utilisation.
Réutilisation de contexte
La réutilisation du contexte est légèrement différente de la réutilisation des fonctionnalités. La réutilisation du contexte est la réutilisation d'une série d'instructions, et diverses opérations sont toujours effectuées entre ces instructions. En d'autres termes, réutilisez les déclarations avant et après divers comportements. Par conséquent, la réutilisation du contexte conduit souvent à une inversion des classes de style de contrôle. La réutilisation du contexte est un moyen très efficace de réutiliser la gestion des exceptions, la connexion et la gestion du cycle de vie des transactions, l'itération et l'arrêt du flux, et de nombreux autres contextes opérationnels communs.
Voici deux méthodes qui sont réalisées avec InputStream:
public void printStream (inputStream inputStream) lève ioException {if (inputStream == null) return; Exception IOException = null; try {int caractères = inputStream.read (); while (caractère! = -1) {System.out.print ((char) Caractère); // différents caractères = inputStream.read (); }} enfin {try {inputStream.close (); } catch (ioException e) {if (exception == null) throw e; }}} public String readStream (inputStream inputStream) lève ioException {stringBuffer tamper = new StringBuffer (); // différent if (inputStream == null) return; Exception IOException = null; try {int caractères = inputStream.read (); while (caractère! = -1) {buffer.append ((char) Caractère); // différents caractères = inputStream.read (); } return buffer.toString (); // différent} enfin {try {inputStream.close (); } catch (ioException e) {if (exception == null) throw e; }}}Les deux méthodes sont différentes de l'opération d'écoulement. Mais le contexte autour de ces opérations est le même. Le code de contexte itère et ferme le sous -stre. En plus des différences dans l'utilisation des marques de commentaires, le code ci-dessus est son code de contexte.
Comme indiqué ci-dessus, le contexte implique une gestion des exceptions et garantit que le flux est correctement fermé après itération. La rédaction de ces erreurs et du code de libération des ressources est encore lourde et sujette aux erreurs. La gestion des erreurs et la gestion correcte des connexions sont plus complexes dans les transactions JDBC. Il est évidemment plus facile d'écrire du code une fois et de le réutiliser n'importe où.
Heureusement, la méthode d'encapsulation du contexte est simple. Créez une classe de contexte et y mettez le contexte public. Dans l'utilisation du contexte, différentes instructions de fonctionnement sont abstraites dans l'interface de fonctionnement, puis chaque opération est encapsulée dans la classe qui implémente l'interface d'opération (appelée classe d'opération ici). Vous n'avez qu'à insérer une instance de la classe d'opération dans le contexte. Cela peut être fait en passant une instance de la classe d'opération en tant que paramètre au constructeur de l'objet de contexte, ou en passant une instance de la classe d'opération en tant que paramètre à la méthode d'exécution spécifique du contexte.
Ce qui suit montre comment séparer l'exemple ci-dessus en contexte et interface opérationnelle. StreamProcessor (Interface Operation) est passé sous forme de paramètre à la méthode ProcessStream () de StreamProcessorContext.
// Interface du plugin de traitement de flux interface publique StreamProcessor {public void Process (int entrée);} // Classe de contexte de traitement de flux Classe publique StreamProcessorContext {// Instancier l'interface de l'opération StreamProcessor et servir en tant que paramètre public void processStream (InputStream InputStream, StreamProcessor Processeur) lance ioexception {if (entrée sous -stream == Null) Retour; Exception IOException = null; try {int caractères = inputStream.read (); while (caractères! = -1) {processeur.process (caractère); caractéristique = inputStream.read (); }} enfin {try {inputStream.close (); } catch (ioException e) {if (exception == null) throw e; lancer une exception; }}}}Vous pouvez maintenant utiliser la classe StreamProcessorContext pour imprimer le contenu du flux comme l'exemple suivant:
FileInputStream inputStream = new FileInputStream ("MyFile"); // Exemple de l'opération via la sous-classe anonyme Implémentation de l'interface StreamProcessor New StreamProcessorContext (). ProcessStream (InputStream, nouveau StreamProcessor () {public void Process (int entrée) {system.out.print ((char) entrée);}});Ou lisez le contenu du flux d'entrée comme celui-ci et ajoutez-le à une séquence de caractères:
La classe publique StreamToStringReader implémente StreamProcessor {private StringBuffer Buffer = new StringBuffer (); public StringBuffer getBuffer () {return this.buffer; } public void Process (int entrée) {this.buffer.append ((char) entrée); }} FileInputStream inputStream = new FileInputStream ("myFile"); StreamToStringReader Reader = new StreamToStringReader (); new StreamProcessorContext (). ProcessStream (InputStream, Reader); // faire quelque chose avec l'entrée de Stream.Reader.GetBuffer ();Comme vous pouvez le voir, faites n'importe quoi avec le flux en insérant une implémentation d'interface StreamProcessor différente différente. Une fois que StreamProcessorContext est entièrement implémenté, vous n'aurez jamais de problème avec les flux non clos.
La réutilisation du contexte est très puissante et peut être utilisée dans de nombreux autres environnements en dehors du traitement des flux. Un cas d'utilisation évident consiste à gérer correctement les connexions et les transactions de la base de données ( open - process - commit()/rollback() - close() ). Les autres cas d'utilisation sont le traitement des canaux Nio et la synchronisation du thread dans les sections critiques ( lock() - access shared resource - unlock() ). Il peut également convertir les exceptions vérifiées de l'API en exceptions non contrôlées.
Lorsque vous recherchez un code adapté à la réutilisation de contexte dans votre projet, recherchez les modes de fonctionnement suivants:
Lorsque vous trouvez un tel modèle, les opérations régulières avant et après peuvent réaliser une réutilisation de contexte.
Contexte comme méthode de modèle
Parfois, vous voudrez avoir plusieurs points de plugin dans le contexte. Si le contexte se compose de nombreuses étapes plus petites et que vous souhaitez que chaque étape du contexte soit personnalisable, vous pouvez implémenter le contexte en tant que méthode de modèle. La méthode du modèle est un motif de conception GoF. Fondamentalement, la méthode du modèle divise un algorithme ou un protocole en une série d'étapes. Une méthode de modèle est généralement implémentée en tant que classe de base et fournit une méthode pour chaque étape d'un algorithme ou d'un protocole. Pour personnaliser n'importe quelle étape, créez simplement une classe qui étend la classe de base de la méthode du modèle et remplacez la méthode de l'étape que vous souhaitez personnaliser.
L'exemple suivant est un jdbccontext implémenté en tant que méthode de modèle. Les sous-classes peuvent remplacer l'ouverture et la fermeture des connexions pour fournir un comportement personnalisé. La méthode ProcessRecord (résultat de résultat) doit toujours être remplacée car elle est abstraite. Cette méthode fournit des opérations qui ne sont pas dans le contexte et qui sont différentes dans différents cas à l'aide de JDBCConText. Cet exemple n'est pas un jdbccontext parfait. Il est uniquement utilisé pour démontrer comment utiliser des méthodes de modèle lors de la mise en œuvre du contexte.
classe abstraite publique jdbcconText {dataSource dataSource = null; // Le constructeur sans paramètres peut être utilisé pour les sous-classes sans dataSource pour obtenir la connexion publique jdbcConText () {} public jdbcConText (dataSource dataSource) {this.datasource = dataSource; } connexion protégé OpenConnection () lève sqlexception {return dataSource.getConnection (); } Protected void CloseConnection (connexion connexion) lève sqlexception {connection.close (); } // processRecord (resultSet Result) méthode protégé Résumé ProcessRecord (resultSet Result) lève la conception de SQLEXIGNE; public void execute (String sql, object [] Paramètres) lève SQException {connexion connexion = null; Déclaration de préparation = null; Resultsset result = null; try {connection = openconnection (); instruction = connection.preparestatement (SQL); for (int i = 0; i <paramètres.length; i ++) {instruction.setObject (i, paramètres [i]); } result = instruction.executequery (); while (result.next ()) {processRecord (result); }} enfin {if (result! = null) {try {result.close (); } catch (sqlexception e) {/ * ignore * /}} if (instruction! = null) {try {instruction.close (); } catch (sqlexception e) {/ * ignore * /}} if (instruction! = null) {try {instruction.close (); } catch (sqlexception e) {/ * ignore * /}} if (connection! = null) {closeConnection (connexion); }}}}Il s'agit d'une sous-classe qui étend JdbcConText pour lire la liste d'utilisateurs:
La classe publique ReadUsers étend jdbcconText {list users = new ArrayList (); Public ReadUsers (DataSource DataSource) {super (dataSource); } public list getUsers () {return this.users; } protégé void processRecord (resultSet result) {user user = new user (); user.setName (result.getString ("name")); user.setEmail (result.getString ("e-mail")); users.add (utilisateur); }}Voici comment utiliser la classe ReadUsers:
ReadUsers readUsers = new ReadUsers (dataSource); readUsers.ExECUTE ("SELECT * FROM Users", nouvel objet [0]); list users = readUsers.getUsers (); Si la classe ReadUsers doit obtenir une connexion à partir du pool de connexion et la remettre au pool de connexion après utilisation, vous pouvez insérer la connexion en remplaçant openConnection() et closeConnection(Connection connection) .
Notez comment réécrire le code d'opération d'insertion via la méthode. La sous-classe de JDBCConText remplace la méthode ProcessRecord pour fournir un traitement d'enregistrement spécial. Dans l'exemple StreamContext, le code d'opération est encapsulé dans un objet séparé et est fourni sous forme de paramètre de méthode. L'objet qui implémente l'interface d'opération StreamProcessor est passé sous forme de paramètre à la méthode ProcessStreamContext processStream(...) .
Vous pouvez utiliser les deux techniques lors de la mise en œuvre du contexte. La classe JDBCConText peut transmettre des objets ConnectionOpener et ConnectionCloser qui implémentent l'interface d'opération en tant que paramètres à la méthode EXECUTE, ou comme paramètres du constructeur. Personnellement, je préfère utiliser des objets de fonctionnement séparés et des interfaces de fonctionnement pour deux raisons. Tout d'abord, il est plus facile de tester le code d'opération seul; Deuxièmement, il rend le code d'opération réutilisable dans plusieurs contextes. Bien sûr, le code d'opération peut également être utilisé à plusieurs endroits du code, mais ce n'est qu'un avantage. Après tout, nous essayons ici de réutiliser le contexte, et non de réutiliser les opérations.
Conclusion
Vous avez maintenant vu deux façons différentes de réutiliser le code. Réutilisation des fonctionnalités classiques et réutilisation de contexte moins commune. Espérons que la réutilisation du contexte sera aussi courante que la réutilisation des fonctionnalités. La réutilisation du contexte est un moyen très utile de résumer le code des détails sous-jacents d'une API (tels que JDBC, IO ou API NIO, etc.). Surtout si l'API contient des ressources qui doivent être gérées (sur et fermer, obtenir et revenir, etc.).
API Persistance / ORM, Mr.Persister utilise la réutilisation du contexte pour réaliser la gestion automatique de la connexion et du cycle de vie des transactions. De cette façon, l'utilisateur n'aura jamais à s'inquiéter d'ouvrir ou de fermer correctement la connexion, ou de commettre ou de faire reculer la transaction. Mr.Persister fournit un contexte dans lequel les utilisateurs peuvent insérer leurs opérations. Ces contextes sont responsables de l'ouverture, de la fermeture, de la validation et du retour.
Le cadre du printemps populaire contient beaucoup de réutilisation de contexte. Par exemple, Springs JDBC Abstraction. Les développeurs de printemps l'utilisent comme un «renversement de contrôle». Ce n'est pas le seul type d'inversion de contrôle utilisé par les cadres de printemps. La caractéristique principale du printemps est les usines de haricots d'injection de dépendance ou les «contextes d'application». L'injection de dépendance est un autre type d'inversion de contrôle.
Ce qui précède est la fonction et la réutilisation de contexte du code Java que l'éditeur vous a présenté. J'espère que cela vous sera utile. Si vous avez des questions, veuillez me laisser un message et l'éditeur vous répondra à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!