Code cleanliness freaks! Crazy to see dozens of if-else’s in a class? Are design patterns useless? When you are asked in the interview, you can only answer the simplest singleton mode. Have you ever used advanced features like reflection? Let design mode (template method mode + factory mode) and reflection help you eliminate if-else! Is really the development of super super super super super useful dry goods ah!
The pit goods
One day, code farmers fat rolling pig received a superior demand, this demand cattle force, one-stop intelligent report query platform, support mysql, PGXL, TIDB, Hive, Presto, Mongo and many other data sources, want what data can all give you find out to show, for business personnel data analysis is of great significance!
Although the parameter verification, query engine and query logic of each data source are different, but Fat Pig is familiar with these frameworks, it is not difficult for her, she only spent a day to finish writing.
The leader, Fat Rolling Bear, also affirmed the efficiency of fat rolling pig. But the good times did not last long, on the third day, the leader was idle, ready to do a code review, but the fat roll bear was shocked, there were nearly 30 if-else code in a class, my goodness, this can make code cleanliness maniac collapse.
// Check the validity of the entry Boolean check =false;
if(DataSourceEnum.hive.equals(dataSource)){
check = checkHiveParams(params);
} else if(DataSourceEnum.tidb.equals(dataSource)){
check = checkTidbParams(params);
} else if(DataSourceEnum.mysql.equals(dataSource)){ check = checkMysqlParams(params); } / /else if. Omit PGXL, presto, etcif(check){
if(DataSourceEnum.hive.equals(dataSource)){
list = queryHive(params);
} else if(DataSourceEnum.tidb.equals(dataSource)){
list = queryTidb(params);
} else if(DataSourceEnum.mysql.equals(dataSource)){ list = queryMysql(params); } / /else if. Omit PGXL, presto, etc.} // Log log.info("User ={} query data source ={} result size={}",params.getUserName(),params.getDataSource(),list.size());
Copy the code
Template patterns come to the rescue
First of all, let’s analyze it. No matter what the data source is, the algorithm structure (process) is the same: 1. Verify the validity of parameters; 2. That doesn’t mean the template is the same, but the details are different, right?
Let’s look at the definition of the template method pattern in the design pattern:
Template method pattern: A framework for defining an algorithm in an operation, deferring some steps to subclasses. Allows subclasses to redefine specific steps of an algorithm without changing its structure. In layman’s terms, you put the same methods of a subclass into its abstract superclass.
Isn’t our requirement similar to the template method pattern? So we can pull the template into the superclass (abstract class). As for the specific steps that are implemented differently, those special steps are overridden by subclasses.
Nonsense not to say, we first write the parent class template well, exactly the same logic is to log, this step in the template write dead good. As for checking parameters and queries, these two methods are different, so they need to be abstract and overridden by subclasses.
public abstract class AbstractDataSourceProcesser <T extends QueryInputDomain> { public List<HashMap> query(T params){ List<HashMap> list = new ArrayList<>(); Boolean b = checkParam(params);if{// query list = queryData(params); } // Log log.info("User ={} query data source ={} result size={}",params.getUserName(),params.getDataSource(),list.size());
returnlist; } // Abstract methods use subclasses to implement specific logic. abstract List<HashMap> queryData(T params); }Copy the code
This code is very simple. But for the sake of newbies, there’s one thing to explain:
This T thing. It’s called generics, because different data sources take different inputs, so we use generics. But they also have common parameters, like a username. Therefore, in order to make better use of common resources without duplication, we can have a generic upper bound on generic design.
public class QueryInputDomain<T> { public String userName; // Query user name public String dataSource; // query data sources such as mysql\tidb public T params; } public class MysqlQueryInput extends QueryInputDomain{private String database; // database public String SQL; //sql }Copy the code
CheckParam (), queryData(), checkParam(), queryData();
@Component("dataSourceProcessor#mysql")
public class MysqlProcesser extends AbstractDataSourceProcesser<MysqlQueryInput>{
@Override
public Boolean checkParam(MysqlQueryInput params) {
System.out.println(Check whether mysql parameters are correct);
return true;
}
@Override
public List<HashMap> queryData(MysqlQueryInput params) {
List<HashMap> list = new ArrayList<>();
System.out.println(Mysql > select * from user where id = 1;);
returnlist; }}Copy the code
In this way, all the data sources are self-contained, with a class of their own, and it is easy and clear to extend the data source or modify the logic of a data source later.
To be honest, the template method pattern is too simple, the abstract class this thing is too basic and common, the general graduating students will know. But for the newcomer entering the workplace, it is really not decisive application in actual production. So a word of caution: be sure to think abstractly and avoid redundant code.
Also, to be verbose, even engineers with a few years of experience can easily make a mistake. If your boss only gave you a mysql data source query requirement at the beginning, and there was no if-else at all, you might not care about it and just write it in a class without thinking about subsequent extensions. It’s not until more and more new requirements come in later that you realize you’re wasting your time refactoring everything. So a reminder: don’t limit your needs to today, but think about the future. High scalability from the start, and subsequent requirements changes and maintenance are fun.
Original statement: this article is [fat rolling pig learning programming] original blog post, reproduced please indicate the source. Make programming fun in comic form! Original is not easy, beg attention!
Factory mode comes to the rescue
But the template pattern is not completely solve the if – else roll fat pig, because need according to the incoming dataSource parameter, to determine which service to implement query logic, it now:
if(DataSourceEnum.hive.equals(dataSource)){
list = queryHive(params);
} else if(DataSourceEnum.tidb.equals(dataSource)){
list = queryTidb(params);
}
Copy the code
So how do you get rid of this if-else? I want to start by telling you the story of factory mode.
Factory pattern: The factory method pattern is a pattern for creating objects that is widely used in the JDK as well as in the Spring and Struts frameworks. It shifts the job of creating objects to the factory class.
In order to echo the word factory, I specially give an example of OEM to let you understand, so that you should have a more profound impression.
Take the mobile phone manufacturing industry. We know that there are Apple phones, Xiaomi phones and so on, and the manufacturing method of each brand of mobile phone must be different. We can first define a standard interface of mobile phone, which has make() method, and then different types of mobile phone inherit this interface:
AbstractProduct: AbstractProduct
public interface Phone {
void make();
}
#MiPhone: Manufacture Xiaomi mobile phone (Product1)
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
System.out.println("make xiaomi phone!"); }}#IPhone: Make IPhone (Product2)
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
System.out.println("make iphone!"); }}Copy the code
Now there is a mobile phone OEM: [Tianba Mobile phone OEM]. The customer will only tell the factory the model of mobile phone, so it needs to match the production plan of different models. Then how does the foundry achieve this? The simple factory pattern (and abstract Factory pattern and factory method pattern, if you are interested) is implemented as follows:
#PhoneFactory: PhoneFactory
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")) {return new MiPhone();
}
else if(phoneType.equalsIgnoreCase("iPhone")) {
returnnew IPhone(); }}}Copy the code
This way, when the customer tells you the phone model, you can call the foundry class method to get the corresponding phone manufacturing class. You’ll notice that it’s really just if-else, but it would be much better maintenance and aesthetics to pull if-else into a factory class that creates objects uniformly without intrusions into our business code
First, we should add the Spring container annotation @Component to each specific dataSourceProcessor, such as MysqlProcesser, TidbProcesser. DataSourceProcessor# data source name = bean name = bean name = bean name = bean name = bean name = bean name = bean name
@Component("dataSourceProcessor#mysql")
public class MysqlProcesser extends AbstractDataSourceProcesser<MysqlQueryInput>{
@Component("dataSourceProcessor#tidb")
public class TidbProcesser extends AbstractDataSourceProcesser<TidbQueryInput>{
Copy the code
What’s the good of that? I can use Spring to help us a one-time load all inherited from AbstractDataSourceProcesser Bean, Shaped like a Map < String, AbstractDataSourceProcesser >, the Key is the name of the Bean, and the Value is the corresponding Bean:
@Service
public class QueryDataServiceImpl implements QueryDataService {
@Resource
public Map<String, AbstractDataSourceProcesser> dataSourceProcesserMap;
public static String beanPrefix = "dataSourceProcessor#"; @Override public List<HashMap> queryData(QueryInputDomain domain) { AbstractDataSourceProcesser dataSourceProcesser = dataSourceProcesserMap.get(beanPrefix + domain.getDataSource()); // omit query code}}Copy the code
In case you still don’t understand, let’s go straight to the effect:
The dataSourceProcesserMap contains all data source beans. The Key is the name of the Bean and the Value is the corresponding Bean.
2, I only need to pass key(i.e., prefix + data source name =beanName), will match the corresponding actuator. DataSourceProcessor# tidb = dataSourceProcesserMap = TidbProcesser = dataSourceProcesserMap = TidbProcesser
public static String classPrefix = "com.lyl.java.advance.service.";
AbstractDataSourceProcesser sourceGenerator =
(AbstractDataSourceProcesser) Class.forName
(classPrefix+DataSourceEnum.getClasszByCode(domain.getDataSource()))
.newInstance();
Copy the code
Note that this method uses a className to get an instance of the class, and the front end does not pass a className. So we can use enumeration classes to define class names for different data sources:
public enum DataSourceEnum {
mysql("mysql"."MysqlProcesser"),
tidb("tidb"."TidbProcesser");
private String code;
private String classz;
Copy the code
Original statement: this article is [fat rolling pig learning programming] original blog post, reproduced please indicate the source. Make programming fun in comic form! Original is not easy, beg attention!
conclusion
Some people always feel that design mode is not useful, because they usually write code only CRUD and CRUD. When you are asked about design mode in the interview, you can only answer the simplest singleton mode and ask if you have ever used advanced features like reflection, and answer yes.
In fact, the JAVA 23 design patterns, each is a classic. Today we solved the if-else crash using template method pattern + factory pattern (or reflection). In the follow-up study of design patterns, you should also practice more and find the application from real projects. Only then can you really take the knowledge as your own.
The content and technical points of this article are very simple, but the purpose of this article is to show that you should have a good code abstraction thinking. Avoid a stack of if-else or other bad code in your code.
Even if you have good code abstraction thinking, when developing requirements, don’t just think about the present, think about future extensibility.
Just like you fall in love, only consider the present is cheating and playing with women’s feelings of the man, consider the future, is a responsible person
“May the world be free of men who play with women’s affections.”
Original statement: this article is [fat rolling pig learning programming] original blog post, reproduced please indicate the source. Make programming fun in comic form! Original is not easy, beg attention!
This article comes from the public account: [Fat rolling pig learning programming]. A set of appearance level and talent in a suit, not smart but hard enough female program yuan. Programming in comic form so easy and interesting! Beg attention!