При создании API вы всегда должны думать о том, кто будет его использовать. Когда API прост и понятен в использовании, пользователи счастливы. Когда пользователи счастливы, тогда все тоже счастливы. Но отличного удобства использования не всегда легко достичь. Есть шаблоны, которые помогают в этом, в этом посте я сосредоточусь на классическом шаблоне компоновщика и на том, как вы можете улучшить его с помощью шаблона компоновщика шагов , чтобы создавать объекты без интерфейса мозга, просты в использовании, невозможно ошибиться . Итак, давайте начнем рисовать некоторый контекст, у нас есть 2 объекта домена, представляющих пользовательскую конфигурацию для подключения к какому-либо удаленному или локальному серверу. Когда требуются удаленные учетные данные, когда локальные нет.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
|
package com.marco.sbp;public class UserConfiguration { private final String name; private ServerDetails serverDetails; public UserConfiguration(String name) { this.name = name; } public void setServerDetails(ServerDetails serverDetails) { this.serverDetails = serverDetails; } public String getName() { return name; } public ServerDetails getServerDetails() { return serverDetails; }} |
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.marco.sbp;public class ServerDetails { private final String host; private String user; private String password; public ServerDetails(String host) { this.host = host; } public void setUser(String user) { this.user = user; } public void setPassword(String password) { this.password = password; } public String getHost() { return host; } public String getUser() { return user; } public String getPassword() { return password; }} |
Мы хотим абстрагировать построение объектов выше, используя 2 различных метода: классический шаблон построения и шаблон построения ступеней.
Классический шаблон построения довольно прост, он маскирует создание UserConfiguration и ServerDetails с использованием методов с правильными именами, таких как onLocalHost, onRemoteHost и т. Д.
|
01
02
03
04
05
06
07
08
09
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
|
package com.marco.sbp.builder;import com.marco.sbp.ServerDetails;import com.marco.sbp.UserConfiguration;public class ClassicBuilder { private String name; private String host; private String user; private String password; public ClassicBuilder(String name){ this.name = name; } public ClassicBuilder onLocalHost(){ this.host = "localhost"; return this; } public ClassicBuilder onRemoteHost(String remoteHost){ this.host = remoteHost; return this; } public ClassicBuilder credentials(String user, String password){ this.user = user; this.password = password; return this; } public UserConfiguration build(){ UserConfiguration userConfiguration = new UserConfiguration(name); ServerDetails serverDetails = new ServerDetails(host); serverDetails.setUser(user); serverDetails.setPassword(password); userConfiguration.setServerDetails(serverDetails); return userConfiguration; }} |
Шаблон построителя шагов все еще использует умные имена для создания объекта, но он предоставляет эти методы только при необходимости, используя интерфейсы и правильную инкапсуляцию.
|
01
02
03
04
05
06
07
08
09
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
|
package com.marco.sbp.builder;import com.marco.sbp.ServerDetails;import com.marco.sbp.UserConfiguration;/** "Step Builder" */public class StepBuilder { public static NameStep newBuilder() { return new Steps(); } private StepBuilder() { } public static interface NameStep { /** * @param name * unique identifier for this User Configuration * @return ServerStep */ ServerStep name(String name); } public static interface ServerStep { /** * The hostname of the server where the User Configuration file is stored will be set to "localhost". * * @return BuildStep */ public BuildStep onLocalhost(); /** * The hostname of the server where the User Configuration file is stored. * * @return CredentialsStep */ public CredentialsStep onRemotehost(String host); } public static interface CredentialsStep { /** * Username required to connect to remote machine Password required to connect to remote machine * * @return BuildStep */ public BuildStep credentials(String user, String password); } public static interface BuildStep { /** * @return an instance of a UserConfiguration based on the parameters passed during the creation. */ public UserConfiguration build(); } private static class Steps implements NameStep, ServerStep, CredentialsStep, BuildStep { private String name; private String host; private String user; private String password; public BuildStep onLocalhost() { this.host = "localhost"; return this; } public ServerStep name(String name) { this.name = name; return null; } public CredentialsStep onRemotehost(String host) { this.host = host; return this; } public BuildStep credentials(String user, String password) { this.user = user; this.password = password; return this; } public UserConfiguration build() { UserConfiguration userConfiguration = new UserConfiguration(name); ServerDetails serverDetails = new ServerDetails(host); serverDetails.setUser(user); serverDetails.setPassword(password); userConfiguration.setServerDetails(serverDetails); return userConfiguration; } }} |
Посмотрим теперь, каково взаимодействие с обоими нашими сборщиками. Классический конструктор будет создан с использованием имени пользовательской конфигурации, затем он предоставит доступ ко всем своим методам, оставляя пользователю слишком свободную возможность выбирать, что будет дальше.
Например, неосторожный пользователь может получить конфигурацию UserConfiguration с localhost, где не требуется проверка подлинности, при этом все еще передавая пользователя и пароль.
Это сбивает с толку и может привести к исключениям во время выполнения.
Вот некоторые из возможных комбинаций UserConfiguration, которые пользователь может получить, некоторые из которых являются правильными, а многие ошибочными:
Совершенно другая история со строителем шагов, здесь представлен только один шаг за раз:
Если учетные данные не нужны, они не будут доступны, и метод build () предлагается только тогда, когда состояние объекта обязательно будет согласованным и полным:
Только 2 возможных UserConfiguration могут быть построены с этим шаблоном, и оба имеют смысл и понятны для пользователя.
Вывод
Шаблон построителя шагов не является заменой классического блоховского, иногда вы хотите заставить пользователя заполнить какой-то параметр, прежде чем приступить к созданию, в этом случае создатель шагов выполняет свою работу, в противном случае, когда требуется более открытый подход чем классический строитель твой парень.





