-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy path01.02-Programming-Bottom-Up.txt
More file actions
216 lines (177 loc) · 15.7 KB
/
01.02-Programming-Bottom-Up.txt
File metadata and controls
216 lines (177 loc) · 15.7 KB
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
1.2 Programming Bottom-Up
1.2 Программирование снизу-вверх
It’s a long-standing principle of programming style that the
functional elements of a program should not be too large. If some
component of a program grows beyond the stage where it’s readily
comprehensible, it becomes a mass of complexity which conceals errors
as easily as a big city conceals fugitives. Such software will be hard
to read, hard to test, and hard to debug.
Давно известное правило методологии программирования состоит в том,
что функциональные элементы программы не должны быть слишком
большими. Если какой-то из элементов программы вырастает настолько,
что перестает быть легким для понимания, то он превращается в
запутанный клубок, скрывающий ошибки так же легко, как большой город
скрывает беглецов. Такое программное обеспечение будет трудно читать,
трудно проверять и трудно отлаживать.
In accordance with this principle, a large program must be divided
into pieces, and the larger the program, the more it must be
divided. How do you divide a program? The traditional approach is
called top-down design: you say “the purpose of the program is to do
these seven things, so I divide it into seven major subroutines. The
first subroutine has to do these four things, so it in turn will have
four of its own subroutines,” and so on. This process continues until
the whole program has the right level of granularity—each part large
enough to do something substantial, but small enough to be understood
as a single unit.
В соответствии с этим правилом большая программа должна быть разбита
на части, и чем больше программа, тем больше частей должно быть. Как
же разбить программу? Традиционный подход называется нисходящим
проектированием. Вы скажете: “Цель программы состоит в том, чтобы
сделать семь таких-то вещей, значит, я разбиваю программу на семь
главных подпрограмм. Первая подпрограмма должна сделать четыре
таких-то вещи, поэтому у нее, в свою очередь, будет четыре собственные
подпрограммы, и так далее”. Этот процесс продолжается до тех пор, пока
вся программа не достигнет надлежащего уровня модульности – каждая
часть достаточно велика, чтобы сделать что-то важное, но и достаточно
мала для того, чтобы представлять собой отдельный модуль.
Experienced Lisp programmers divide up their programs differently. As
well as top-down design, they follow a principle which could be called
bottom-up design—changing the language to suit the problem. In Lisp,
you don’t just write your program down toward the language, you also
build the language up toward your program. As you’re writing a program
you may think “I wish Lisp had suchand-such an operator.” So you go
and write it. Afterward you realize that using the new operator would
simplify the design of another part of the program, and so
on. Language and program evolve together. Like the border between two
warring states, the boundary between language and program is drawn and
redrawn, until eventually it comes to rest along the mountains and
rivers, the natural frontiers of your problem. In the end your program
will look as if the language had been designed for it. And when
language and program fit one another well, you end up with code which
is clear, small, and efficient.
Опытные Лисп-программисты разбивают свои программы по-другому. Так же,
как и в нисходящем проектировании, они следуют принципу, который можно
было бы назвать восходящим проектированием – изменением языка для
решения задачи. В Лисп вы не просто подгоняете свою программу к языку,
вы также создаете язык для нее. По мере написания программы вам может
прийти в голову: “Мне бы хотелось, чтобы в Лисп был такой-то
оператор”. Тогда вы идете и пишете его. Позже вы понимаете, что
использование нового оператора упростило бы разработку другой части
программы и так далее. Язык и программа развиваются вместе. Как
граница между двумя враждующими государствами, граница между языком и
программой чертится и перечерчивается, пока в конечном счете черта не
остается вдоль гор и рек – естественных границ вашей задачи. В итоге
ваша программа будет выглядеть так, как будто язык был разработан
специально для нее. И когда язык и программа будут хорошо
соответствовать друг другу, вы получите ясный, маленький и эффективный
код.
It’s worth emphasizing that bottom-up design doesn’t mean just writing
the same program in a different order. When you work bottom-up, you
usually end up with a different program. Instead of a single,
monolithic program, you will get a larger language with more abstract
operators, and a smaller program written in it. Instead of a lintel,
you’ll get an arch.
Стоит подчеркнуть, что восходящее проектирование не означает всего
лишь написание той же программы в другом порядке. Когда вы работаете в
восходящем стиле, то в итоге, как правило, получите другую
программу. Вместо единственной, монолитной программы вы получите более
объемный язык с более абстрактными операторами и более короткую
программу, написанную на нем. Вместо перемычки у вас будет арка.
In typical code, once you abstract out the parts which are merely
bookkeeping, what’s left is much shorter; the higher you build up the
language, the less distance you will have to travel from the top down
to it. This brings several advantages:
В типичном коде, как только вы обобщаете части, являющиеся просто
бухгалтерией, то что остается намного короче FIXME; чем выше вы
создаете язык, тем меньше расстояния вы должны будете проходить сверху
вниз. Это дает несколько преимуществ:
1. By making the language do more of the work, bottom-up design yields
programs which are smaller and more agile. A shorter program doesn’t
have to be divided into so many components, and fewer components means
programs which are easier to read or modify. Fewer components also
means fewer connections between components, and thus less chance for
errors there. As industrial designers strive to reduce the number of
moving parts in a machine, experienced Lisp programmers use bottom-up
design to reduce the size and complexity of their programs.
1. Заставляя язык быть производительнее, восходящее проектирование
помогает создать более шустрые и менее объемные программы. Небольшую
программу ни к чему разбивать на слишком большое число компонент, ведь
меньшее число компонент означает, что программы легче прочитать или
изменить. Также, чем меньше число компонент, тем меньше связей между
ними и, таким образом, меньше шанс на ошибки в этих местах. Как
промышленные проектировщики стремятся сократить количество движущихся
частей в машине,так и опытные программисты Лисп используют восходящее
проектирование с целью уменьшения размера и сложности своих программ.
2. Bottom-up design promotes code re-use. When you write two or more
programs, many of the utilities you wrote for the first program will
also be useful in the succeeding ones. Once you’ve acquired a large
substrate of utilities, writing a new program can take only a fraction
of the effort it would require if you had to start with raw Lisp.
2. Восходящий дизайн способствует многократному использованию
кода. Когда вы пишете две или более программы, многие из утилит,
написанных вами для первой программы, пригодятся и для
последующих. Собрав однажды большое количество утилит, на создание
новой программы вы потратите лишь часть тех усилий, которые бы
потребовались, начни вы писать на Лисп с нуля.
3. Bottom-up design makes programs easier to read. An instance of this
type of abstraction asks the reader to understand a general-purpose
operator; an instance of functional abstraction asks the reader to
understand a special-purpose subroutine.1)
3. Восходящий дизайн делает программы более легкими для чтения. В
случае этого типа абстракции требуется, чтобы читатель понял оператор
общего назначения; в случае функциональной абстракции нужно, чтобы
читатель понял подпрограмму специального назначения.1)
1) "But no one can read the program without understanding all your new
utilities." To see why such statements are usually mistaken, see
Section 4.8.
1) “Но никто не может читать программу без понимания всех ваших новых
утилит.” Чтобы понять, почему такие утверждения обычно ошибочны,
смотрите Раздел 4.8.
4. Because it causes you always to be on the lookout for patterns in
your code, working bottom-up helps to clarify your ideas about the
design of your program. If two distant components of a program are
similar in form, you’ll be led to notice the similarity and perhaps to
redesign the program in a simpler way.
4. Заставляя вас всегда быть в поисках шаблонов в вашем коде,
восходящее проектирование помогает прояснить ваши идеи о модели
программы. Если два отдаленных компонента программы похожи по форме,
то вы первым заметите сходство и, возможно, перепроектируете программу
более простым способом.
Bottom-up design is possible to a certain degree in languages other
than Lisp. Whenever you see library functions, bottom-up design is
happening. However, Lisp gives you much broader powers in this
department, and augmenting the language plays a proportionately larger
role in Lisp style—so much so that Lisp is not just a different
language, but a whole different way of programming.
Восходящее проектирование на языках, отличных от Лисп, осуществимо
лишь до определенной степени. Всякий раз, когда вы видите библиотечные
функции, происходит восходящее проектирование. Однако Лисп дает вам
намного более широкие возможности в этом случае, и расширяемый язык
играет соответственно большую роль в технике программирования на Лисп,
настолько большую, что Лисп – это не просто другой язык, а совершенно
иной способ программирования.
It’s true that this style of development is better suited to programs
which can be written by small groups. However, at the same time, it
extends the limits of what can be done by a small group. In The
Mythical Man-Month, Frederick Brooks proposed that the productivity of
a group of programmers does not grow linearly with its size. As the
size of the group increases, the productivity of individual
programmers goes down. The experience of Lisp programming suggests a
more cheerful way to phrase this law: as the size of the group
decreases, the productivity of individual programmers goes up. A small
group wins, relatively speaking, simply because it’s smaller. When a
small group also takes advantage of the techniques that Lisp makes
possible, it can win outright.
Это правда, что этот способ разработки лучше подходит для программ,
которые могут быть написаны небольшими группами. Однако, в то же самое
время, он расширяет пределы того, что может быть сделано небольшой
группой. В книге “Мифический человеко-месяц” Фредерик Брукс (Frederick
Brooks) предположил, что производительность группы программистов не
растет линейно с ее размером. С увеличением размера группы понижается
производительность отдельных программистов. Опыт программирования на
Лисп предлагает более жизнерадостную формулировку этого закона: с
уменьшением размера группы производительность индивидуальных
программистов повышается. Небольшая группа побеждает, собственно
говоря, просто потому, что она меньше. Когда небольшая группа
воспользуется еще и методиками, которые делает доступными Лисп, то она
может победить вчистую.