При создании 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 могут быть построены с этим шаблоном, и оба имеют смысл и понятны для пользователя.
Вывод
Шаблон построителя шагов не является заменой классического блоховского, иногда вы хотите заставить пользователя заполнить какой-то параметр, прежде чем приступить к созданию, в этом случае создатель шагов выполняет свою работу, в противном случае, когда требуется более открытый подход чем классический строитель твой парень.