If you find that you have a lot of duplicated code, you might consider using a template method to eliminate error-prone duplicate code. Here's an example: The following two classes complete almost the same functionality:
- Instantiate and initialize a reader to read the CSV file;
- Read each line and parse;
- Fills the characters of each line into the product or Customer object;
- Add each object to the set;
- Returns the set.
As you can see, only the annotated place is different. All other steps are the same.
Productcsvreader.java
Public classProductcsvreader {Set<Product> getAll (file file)throwsIOException {Set<Product> Returnset =NewHashset<>(); Try(BufferedReader reader =NewBufferedReader (Newfilereader (file))) {String Line=Reader.readline (); while(Line! =NULL&&!line.trim (). Equals ("") ) {string[] tokens= Line.split ("\\s*,\\s*"); //differentProduct Product =NewProduct (Integer.parseint (Tokens[0]), tokens[1],NewBigDecimal (tokens[2])); Returnset.add (product); Line=Reader.readline (); } } returnReturnset; }}
Customercsvreader.java
Public classCustomercsvreader {Set<Customer> getAll (file file)throwsIOException {Set<Customer> Returnset =NewHashset<>(); Try(BufferedReader reader =NewBufferedReader (Newfilereader (file))) {String Line=Reader.readline (); while(Line! =NULL&&!line.trim (). Equals ("") ) {string[] tokens= Line.split ("\\s*,\\s*"); //differentCustomer customer =NewCustomer (Integer.parseint (Tokens[0]), tokens[1], tokens[2], tokens[3]); Returnset.add (customer); Line=Reader.readline (); } } returnReturnset; }}
for this example, there are only two entities, but a real system may have dozens of entities, so there are a lot of repetitive and error-prone code. You may find that the DAO layer has the same situation, and that each DAO is almost the same operation when it is being checked for deletions, and the only difference is the entity and the table. Let's refactor the annoying code. According to one of the principles mentioned in the first part of the GOF design pattern, we should "encapsulate different concepts" between Productcsvreader and Customercsvreader, unlike annotated code. So what we're going to do is put the same into one class, different to the other. We start by writing Productcsvreader, and we use Extract method to extract the annotated part:
Productcsvreader.java after Extract Method
Public classProductcsvreader {Set<Product> getAll (file file)throwsIOException {Set<Product> Returnset =NewHashset<>(); Try(BufferedReader reader =NewBufferedReader (Newfilereader (file))) {String Line=Reader.readline (); while(Line! =NULL&&!line.trim (). Equals ("") ) {string[] tokens= Line.split ("\\s*,\\s*"); Product Product=Unmarshall (tokens); Returnset.add (product); Line=Reader.readline (); } } returnReturnset; } product Unmarshall (string[] tokens) {Product Product=NewProduct (Integer.parseint (Tokens[0]), tokens[1], NewBigDecimal (tokens[2])); returnproduct; }}
now that we've separated the same (duplicated) code from the different (unique) code, we're going to create a parent class Abstractcsvreader, It includes the same parts as two classes (Productreader and Customerreader). We define it as an abstract class, because we don't need to instantiate it. We will then use the pull-up method to refactor the parent class.
Abstractcsvreader.java
Abstract classAbstractcsvreader {Set<Product> getAll (file file)throwsIOException {Set<Product> Returnset =NewHashset<>(); Try(BufferedReader reader =NewBufferedReader (Newfilereader (file))) {String Line=Reader.readline (); while(Line! =NULL&&!line.trim (). Equals ("") ) {string[] tokens= Line.split ("\\s*,\\s*"); Product Product=Unmarshall (tokens); Returnset.add (product); Line=Reader.readline (); } } returnReturnset; }}
Productcsvreader.java after pull up Method
Public class extends Abstractcsvreader { product unmarshall (string[] tokens) { new product (Integer.parseint ( Tokens[0]), tokens[1], new BigDecimal (tokens[2]); return product; }}
if there is no ' unmarshall ' method in the subclass, the class cannot compile (it calls the Unmarshall method), so we will create an abstract method called Unmarshall .
Abstractcsvreader.java with abstract Unmarshall method
Abstract classAbstractcsvreader {Set<Product> getAll (file file)throwsIOException {Set<Product> Returnset =NewHashset<>(); Try(BufferedReader reader =NewBufferedReader (Newfilereader (file))) {String Line=Reader.readline (); while(Line! =NULL&&!line.trim (). Equals ("") ) {string[] tokens= Line.split ("\\s*,\\s*"); Product Product=Unmarshall (tokens); Returnset.add (product); Line=Reader.readline (); } } returnReturnset; } AbstractProduct unmarshall (string[] tokens);}
now, at this point, Abstractcsvreader is the parent class of Productcsvreader, but not the parent class of Customercsvreader. If Customercsvreader inherits Abstractcsvreader compilation will error. To solve this problem we use generics.
Abstractcsvreader.java with generics
Abstract classAbstractcsvreader<t>{Set<T> getAll (file file)throwsIOException {Set<T> Returnset =NewHashset<>(); Try(BufferedReader reader =NewBufferedReader (Newfilereader (file))) {String Line=Reader.readline (); while(Line! =NULL&&!line.trim (). Equals ("") ) {string[] tokens= Line.split ("\\s*,\\s*"); T element=Unmarshall (tokens); Returnset.add (product); Line=Reader.readline (); } } returnReturnset; } AbstractT unmarshall (string[] tokens);}
Productcsvreader.java with generics
Public class extends Abstractcsvreader<product> { @Override Product unmarshall (string[] tokens) { new Product (Integer.parseint (Tokens[0]), tokens[1], new BigDecimal (tokens[2])); return product;} }
Customercsvreader.java with generics
Public class extends Abstractcsvreader<customer> { @Override Customer unmarshall (string[] tokens) { new Customer (Integer.parseint (Tokens[0]), tokens[1], tokens[2], tokens[3]); return customer; }}
That's what we want! No more duplicate code! The method in the parent class is "template", which contains the immutable code. Those things that change are implemented as abstract methods in subclasses. Remember, when you refactor, you should have automated unit tests to ensure that you don't break your code. I use JUnit, you can use the code I post here, or you can find some examples of other design patterns in this GitHub library . Before I finish, I want to talk about the drawbacks of the template approach. The template method relies on inheritance and suffers from the fragile Base Class problem. Simply put, modifying a parent class can have unintended undesirable effects on the subclass that inherits it. In fact, one of the basic design principles of the GOF design pattern advocates "multi-use combination less inheritance", and many other design patterns also tell you how to avoid code duplication while allowing complex or error-prone code to rely on inheritance as little as possible. Welcome to exchange so that I can improve the quality of my blog.
The original address;Template Method Pattern Example Using Java generics
The translation is not good, welcome to shoot Bricks!