Some design patterns programmers should know
- Tram Ho
As a programmer, our task is to solve the given problems. Many problems have been solved by other developers, so why do we need to solve them. We all don’t want to “reinvent the wheel.” Design pattern will help solve this problem. Let’s learn some design patterns that we should know through examples to better understand them.
1. Singleton
- This is a design pattern that is quite popular. Lots of framwork using this pattern design. This pattern is used when we want to create an object from a class and want to make sure that only one object is created from it. Implement for this design pattern
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class SingletonSample { Singleton private SingletonSample instance = null; private SingletonSample () { } public static SingletonSample getInstance () { if (instance == null) { instance = new SingletonSample (); } return instance; } } |
A construtor is private to prevent access from outside. It is also necessary to create a static variable and the getInstance () method ensures that only one instance of the class is created.
2. Initialization On Demand Holder
This pattern is quite similar to the above Singleton, but it has an advantage over that when it comes to the thread, this pattern will help thread safe, the case of singleton if not in sync can create 2 different instances. For example, Initialization On Demand Holder is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 | public class SingletonSample { private SingletonSample () { } public static SingletonSample getInstance () { return SingletonSampleHolder.INSTANCE; } private static class SingletonSampleHolder { private static final SingletonSample INSTANCE = new SingletonSample (); } } |
True to the name of this pattern, it will not initialize the instance until the getInstance () method is called, with this advantage it helps thread safe.
3. Strategy and factory pattern
- These two patterns are very popular, consider for example when they are combined
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // class represents the house public class House implements Building { public String getType () { return "house" } } // class represents the castle public class Edifice implements Building { public String getType () { return "edifice" } } public class BuildingFactory { private static Map <String, Building> instances; static { instances = new HashMap <> (); instances.put ("house", new House ()); instances.put ("edifice", new Edifice ()); } public static <T extends Building> T getBuilding (String type) { return (T) instances.get (type); } } // Initialize the object Building building = BuildingFactory.getBuilding ("house"); |
If you need a certain type of building, you only need to pass it on and it will return an object of that type or null if there is no instance for this type, it will be very useful in case of using polymorphism.
Design Pattern – Builder
4. Chain of responsibility
When building an application with a lot of business logic, there are a lot of complex logic behind the implementation, this complexity can make code difficult to understand and hard to track, log bug … This pattern will help your code We can be broken down into sections and manage them step by step.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public interface Command <T> { boolean execute (T context); } public class FirstCommand implements Command <Map <String, Object >> { public boolean execute (Map <String, Object> context) { // doing something in here } } public class SecondCommand implements Command <Map <String, Object >> { public boolean execute (Map <String, Object> context) { // doing something in here } } public class Chain { public List <Command> commands; public Chain (Command ... commands) { this.commands = Arrays.asList (commands); } public void start (Object context) { for (Command command: commands) { boolean shouldStop = command.execute (context); if (shouldStop) { return; } } } } Chain chain = new Chain (new FirstCommand (), new SecondCommand ()); Map <String, Object> context = new HashMap <> (); context.put ("some parameter", "some value"); chain.start (context); |
We have broken the code when implementing the method of interface Commands and separated its logic into one place. We can also reorder code if we want to make code decoupled more.
5. Builder
- Many classes when creating an object need to pass many parameters, in which case when we use contructor or use get, set, it makes the code become quite tangled and lengthy. Builder pattern will help us solve this problem. Consider the example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | // We have a Product class public class Product { private String id; private String name; private String description; private Double value; private Product (Builder builder) { setId (builder.id); setName (builder.name); setDescription (builder.description); setValue (builder.value); } public static Builder newProduct () { return new Builder (); } public String getId () { return id; } public void setId (String id) { this.id = id; } public String getName () { return name; } public void setName (String name) { this.name = name; } public String getDescription () { return description; } public void setDescription (String description) { this.description = description; } public Double getValue () { return value; } public void setValue (Double value) { this.value = value; } // defines an inner class within the product class public static final class Builder { private String id; private String name; private String description; private Double value; private Builder () { } public Builder id (String id) { this.id = id; return this; } public Builder name (String name) { this.name = name; return this; } public Builder description (String description) { this.description = description; return this; } public Builder value (Double value) { this.value = value; return this; } public Product build () { return new Product (this); } } } // When you want to create an object, just declare it as follows Product product = Product.newProduct () .id (1l) .description ("TV 46 '") .value (2000.00) .name ("TV 46 '") .build (); // Looking at it, it can be seen that the code is quite understandable in describing fields in case of needing many variables to be transmitted |
6. Template method
This pattern is applied in case we have many common methods but differ in their behavior, this pattern is completely based on polymorphism.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public abstract class Animal { public abstract void makeSound (); public abstract void eatFood (); public abstract void sleep (); public void doEveryday () { makeSound (); eatFood (); sleep (); } } public class Dog extends Animal { public void makeSound () { // bark! } public void eatFood () { // eat dog food } public void sleep () { // sleep a lot! } } |
7. State
- Many objects have their own state. For example, the radio has two states: on (on) and off (off). We will represent it in object-oriented form
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | // an interface for the state of the radio public interface RadioState { void execute (Radio radio); } // class radio public class Radio { private boolean on; private RadioState state; public Radio (RadioState state) { this.state = state; } public void execute () { state.execute (this); } public void setState (RadioState state) { this.state = state; } public void setOn (boolean on) { this.on = on; } public boolean isOn () { return on; } public boolean isOff () { return! on; } } // 2 classes for radio status public class OnRadioState implements RadioState { public void execute (Radio radio) { // throws exception if radio is already on radio.setOn (true); } } public class OffRadioState implements RadioState { public void execute (Radio radio) { // throws exception if radio is already off radio.setOn (false); } } // When executing the result as follows Radio radio = new Radio (new OffRadioState ()); // initial status radio.setState (new OnRadioState ()); radio.execute (); // radio on radio.setState (new OffRadioState ()); radio.execute (); // radio off |
The example above may be simple but in the case of multiple states that can be very helpful, for example we can condition the state, a state is transferred when another state is executed, For example, in the above case, the on state is only executed when the off state is available or the exception is fired, we can also execute any operation that we want.
8. Conclusion
Above are the knowledge to learn some useful patterns through examples. Hopefully the article will be useful for everyone.
Reference
https://medium.com/educative/the-7-most-important-software-design-patterns-d60e546afb0e http://www.thedevpiece.com/design-patterns-that-every-developer-should-know/ https : //www.tutorialspoint.com/design_pattern/index.htm
Source : viblo