Design patterns are often described as "commonly encountered problems and their solutions." When designing, there is a problem first, and we look for a solution (pattern) for it. There are many articles summarized by pattern → problem → example, but I felt that there are few articles summarized by problem → pattern → example, so I wrote it. It's just in case I face the same problem myself, but I hope it helps. I would appreciate it if you could let me know if there is something wrong or something that can be improved. In addition, only the case where the GoF design pattern is applied when designing in Java is dealt with here. Also, please refer to here as we will not write a list of patterns or a description of each pattern.
Will be added at any time
before
public class FooClient {
private final String url;
private final String proxyHost;
private final int proxyPort;
public FooClient(String url) {
this.url = url;
this.proxyHost = null;
this.proxyPort = -1;
}
public FooClient(String url, String proxyHost, int proxyPort) {
this.url = url;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
}
new FooClient(url);
new FooClient(url, proxyHost, proxyPort);
I don't think it's bad for a normal implementation, but there are cases where you want to be clear with and without proxies.
after :bulb: Factory Method
public class FooClient {
private final String url;
private final String proxyHost;
private final int proxyPort;
private FooClient(String url, String proxyHost, int proxyPort) {
this.url = url;
this.proxyHost = proxyHost;
this.proxyPort = proxyPort;
}
public static FooClient withoutProxy(String url) {
return new FooClient(url, null, -1);
}
public static FooClient withProxy(String url, String proxyHost, int proxyPort) {
return new FooClient(url, proxyHost, proxyPort);
}
}
FooClient.withoutProxy(url);
FooClient.withProxy(url, proxyHost, proxyPort);
It is easier to understand whether or not a proxy is used. (Especially when "not used")
before
public class FooDateFilter extends FooFilter {}
public class FooNumberFilter extends FooFilter {}
public class FooStringFilter extends FooFilter {}
FooFilter filter = new FooStringFilter("foo");
It would be nice to have an interface that returns an instance of the subclass according to the argument.
after :bulb: FactoryMethod
public class FooDateFilter extends FooFilter {}
public class FooNumberFilter extends FooFilter {}
public class FooStringFilter extends FooFilter {}
public class FooFilterFactory {
private FooFilterFactory() {}
public static FooFilter create(Date date) {
return new FooDateFilter(date);
}
public static FooFilter create(Number number) {
return new FooNumberFilter(number);
}
public static FooFilter create(String string) {
return new FooStringFilter(string);
}
}
FooFilter filter = FooFilterFactory.create("foo");
Users no longer have to know what's in a subclass. Also, if one of the subclasses is abolished in the future and you want to use a new class, you can just modify the Factory and the user will not be aware of it.
before
public class FooWindow {
private final int width;
private final int height;
private final FooColor color;
private final FooColor backgroundColor;
public FooWindow() {
this.width = 0;
this.height = 0;
this.color = null;
this.backgroundColor = null;
}
public FooWindow(int width, int height, FooColor color, FooColor backgroundColor) {
this.width = width;
this.height = height;
this.color = color;
this.backgroundColor = backgroundColor;
}
public FooWindow withWidth(int width) {
return new FooWindow(width, this.height, this.color, this.backgroundColor);
}
public FooWindow withHeight(int height) {
return new FooWindow(this.width, height, this.color, this.backgroundColor);
}
Below color,Similar method for backgroundColor
}
new FooWindow()
.withWidth(100)
.withHeight(200)
.withColor(color)
.withBackgroundColor(backgroundColor);
It's useless to instantiate every time. If mutable is acceptable, you can substitute it in your own field with withWidth. (It's not a GoF pattern, but it's a pattern called Fluent Setter or Wither)
after :bulb: Builder
public class FooWindow {
private final int width;
private final int height;
private final FooColor color;
private final FooColor backgroundColor;
public FooWindow(int width, int height, FooColor color, FooColor backgroundColor) {
this.width = width;
this.height = height;
this.color = color;
this.backgroundColor = backgroundColor;
}
public static class FooWindowBuilder {
private int width;
private int height;
private FooColor color;
private FooColor backgroundColor;
public FooWindowBuilder withWidth(int width) {
this.width = width;
return this;
}
public FooWindowBuilder withHeight(int height) {
this.height = height;
return this;
}
Below color,Similar method for backgroundColor
public FooWindow build() {
return new FooWindow(this.width, this.height, this.color, this.backgroundColor);
}
}
}
new FooWindowBuilder()
.withWidth(100)
.withHeight(200)
.withColor(color)
.withBackgroundColor(backgroundColor)
.build();
Instance generation has been suppressed. If the number of fields is four as in this example, I think that there is still no problem with the constructor, but as the number increases further, it becomes difficult to know what number argument represents what. In such cases, consider the Builder pattern.
Recommended Posts