1. Definition:
An instance of which product class is created is determined by a factory object.
2. Types:
Creative, but not GOF23 design pattern
3. Use scenarios:
The factory class is responsible for creating fewer objects. The client (application layer) only knows the parameters that are passed into the factory class, and does not care about how to create the object (logic).
4. Advantages:
You just need to pass in a correct parameter to get the object you need without knowing the details of its creation.
5. Disadvantages:
The responsibility of the factory class is relatively heavy. Adding new products needs to modify the judgment logic of the factory class, which violates the principle of opening and closing.
6. Examples:
Create an abstract class Video.java:
package com.design.pattern.creational.simplefactory; public abstract class Video { public abstract void produce(); }
Create implementation classes JavaVideo, PythonVideo:
package com.design.pattern.creational.simplefactory; public class JavaVideo extends Video { @Override public void produce() { System.out.println("Recording Java Course Video"); } }
package com.design.pattern.creational.simplefactory; public class PythonVideo extends Video { @Override public void produce() { System.out.println("Recording Python Course Video"); } }
Then create the factory class VideoFactory.java:
package com.design.pattern.creational.simplefactory; public class VideoFactory { public Video getVideo(String type) { if ("java".equalsIgnoreCase(type)) { return new JavaVideo(); } else if ("python".equalsIgnoreCase(type)) { return new PythonVideo(); } return null; } }
Write tests:
package com.design.pattern.creational.simplefactory; public class Test { public static void main(String[] args) { VideoFactory videoFactory = new VideoFactory(); Video video = videoFactory.getVideo("java"); if (video == null) { return; } video.produce(); } }
As you can see, simple factories can get different results according to the incoming parameters. Clients (application layer) do not need to understand the implementation logic, but if we want to add Web courses, we need to modify the Factory class, which brings risks and does not meet the Open-Close Principle. What we are pursuing is to be the most open, to modify the closure, this time we need to use the factory mode to achieve.
In addition, we can use reflection to compensate for the scalability of simple factories:
Modify VideoFactory as follows:
package com.design.pattern.creational.simplefactory; public class VideoFactory { public Video getVideo(Class c) { Video video = null; try { video = (Video) Class.forName(c.getName()).newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return video; } /** public Video getVideo(String type) { if ("java".equalsIgnoreCase(type)) { return new JavaVideo(); } else if ("python".equalsIgnoreCase(type)) { return new PythonVideo(); } return null; } */ }
Test Test was modified to:
package com.design.pattern.creational.simplefactory; public class Test { public static void main(String[] args){ /** VideoFactory videoFactory = new VideoFactory(); Video video = videoFactory.getVideo("java"); if (video == null) { return; } video.produce(); */ VideoFactory videoFactory = new VideoFactory(); Video video = videoFactory.getVideo(JavaVideo.class); video.produce(); } }
In this way, if an extension occurs, you just need to pass in a new class, and the former Factory class does not need to be changed.
7. Application of JDK Source Code
In the JDK source code, in the Calendar class, we can look at the getInstance() method:
public static Calendar getInstance() { return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); }
When you go into createCalendar, you can see a piece of code like this: createCalendar, createCalendar, createCalendar, createCalendar, createCalendar, createCalendar, createCalendar.
Calendar cal = null; if (aLocale.hasExtensions()) { String caltype = aLocale.getUnicodeLocaleType("ca"); if (caltype != null) { switch (caltype) { case "buddhist": cal = new BuddhistCalendar(zone, aLocale); break; case "japanese": cal = new JapaneseImperialCalendar(zone, aLocale); break; case "gregory": cal = new GregorianCalendar(zone, aLocale); break; } } }
As you can see, for different inputs, different types are returned, because the time zone is fixed and there is no need to consider the scalability of creating Canlendar.
In addition, when we write JDBC, the load driver writes as follows:
Class.forName("com.mysql.jdbc.Driver");
If it's another driver class, write another address, and then call the DriverManager's getConnection method. Its implementation is similar to the reflection of simple factory mode.