It may seem strange that we did not specifically identify modular programming in the review of styles. However, it is obvious that the requirement to develop programs in which stand-alone and, accordingly, easily replaced by others fragments that solve logically closed tasks are identified, is a general logical and general technological, and not exceptional, inherent in any special method .
This requirement is usually interpreted as a modular software product. The advantages of modularity, understood in a general sense, are the possibility of replacing a module without changing the rest and reusing software fragments. The latter can already be considered as a special programming style for which modularity is one of the means.
Means of supporting modularity do not even characterize a style, but a specific language that supports this style. Therefore, it is legitimate to speak not about style, but about a specific language, to what extent it supports the modular construction of programs within its style.
The style of reuse is characterized by the fact that when drafting a program, they strive to make the most of what has already been done - by the programmer himself , his colleagues, or elsewhere. It has long been generally accepted that, ideally, programming as an assembly from pre-prepared blocks — assembly programming — should replace programming as the coding of algorithms. This term was introduced in 1978. G. S. Zeitin later separated this concept from theoretical considerations and introduced it from the programmer side. Note that mathematics have long abandoned the construction of new theorems (corresponding to computer science programs) and new concepts (corresponding to abstract data types) from scratch. In mathematics, previously obtained results are reused in a very professional manner. Here, due to the conceptual unity of the system of concepts and strict criteria of validity, there is no problem of compatibility of new versions, which often ruins attempts not only to reuse, but also simply to use old programs.
Nevertheless, it is interesting to do the following thought experiment: what would happen if mathematicians worked in approximately the same conditions as programmers?
Well, it is clear that if the mathematician, who published the theorem, would take money (and even more would require it in advance) for each use, then reuse would be immediately and immediately reduced.
Even if he didn’t take money for the theorem by itself, but would classify its evidence and threaten criminal prosecution to anyone who dares to understand (`disassemble ') the text he wrote, the situation would have come to the same dead center. the words `deadlock 'are better than the Russian word` dead end').
Apparently, it would be enough to pay for the work of mathematicians on a time-based basis, checking the validity of the reports on the time spent on the number of lines of written evidence. This, perhaps, would not cease re-use completely, but any mathematician (and not just rogues) would rewrite other people's proofs with his own terms and with small changes at every opportunity. Here to the dead point would come more slowly, but equally inevitable.
As you see, we can make an important social and practical conclusion: the only chance for curing the current adolescent programming diseases is the work of open software communities and open project documentation .
It is clear that when using prefabricated building blocks losses are possible. Nevertheless, the potential benefit from reuse is simply enormous. In mathematics, where there are accurate estimates, it is shown that the length of the proof (read, the programs) can be reduced to the TOWER EXPONENT ONCE without significant loss of efficiency only by introducing lemmas (read, using the already constructed programs).
Consider two real-world situations that demonstrate development that is not oriented and re-oriented. In the first situation, there is a programmer who works with a very developed system that has everything. Nevertheless, he writes his own procedure for the lexicographical ordering of strings, because "it is easier to write it yourself than to find it, and then you have to adjust it as well." Conclusion: firstly, there is no desire to reuse here, and secondly, reuse can be hindered by the difficulty of finding out what needs to be included in the program being compiled, as well as version compatibility issues.
The second situation is the other extreme. Java programmer needed to build a syntax analysis. On the question of how he does it, an answer was received: "Why do I need to know this? I have a JavaCC package that does everything as it should!" At the same time, further questioning showed that this programmer does not even imagine what type of analysis method supports JavaCC, and, therefore, can say nothing about how the grammar assignment for this package is related to the efficiency of the analysis. Having learned the possible options, the programmer became thoughtful, but did not change anything. Why? The answer is simple: "So everything is already working!" In short, the quality of use of ready-made components of the system depends on knowledge of them .
The situation is again the same as in mathematics, especially in applied one. The qualified use of theoretical results requires knowledge of the relevant theory, and sometimes ideas of evidence of the results (since in a real situation theories of the theory are never exactly fulfilled). The depth of knowledge required is different: sometimes a fairly general idea, such as when using mathematical functions, sometimes you need information about the principles of implementation, but you can always specify the level of familiarity with the reusable.
A comparison of the situations shows that there are few wishes and guidelines for reuse. Knowledge is required of which portable components can be used and how. Getting the same knowledge often requires substantial labor costs. It is necessary that the software itself being reused should be adapted for this purpose, in particular, that it should follow good practice accumulated in mathematics, and not ignore it as a pure theory .
Deciphering the previous sentence will allow the thoughtful reader to derive all the conditions necessary to ensure reuse in a specific situation. Any instructions here are useless (if a person has not yet realized the previous one) or are harmful (if he thinks he has realized, in fact, he does not understand anything). You can only give general advice for those who are still engaged in (self) education.
In mathematics, for a programmer, not so much specific results are important (they can also be referred to in the reference literature), as the structure of concepts and evidence, the ways in which abstractions are introduced and the use of exclusively abstract concepts in particular situations .
Sometimes the use of advanced language tools contributes to reuse. In particular, C ++ and Java quite often allow you to transfer programs from one operating environment to another (transfer is one of the forms of reuse). But those who are really involved in the transfer of programs, will certainly remember when reading the previous sentence annoying inconsistencies, which C ++ and Java do not help in identifying and preventing.
Re-use contributes to the improvement of the level of language concepts, at least to the second or third type (object-oriented language). Sometimes OOP also helps with the possibilities of inheritance of properties and methods of objects (but often in the most critical situations, a poorly thought-out concept of inheritance hinders so much).
All this is the germ of support for a programming style aimed at reuse. But in the main, the current practice largely hinders reuse, and the level of support systems is not yet high enough and inadequate to the overall task of applying this style.
Reuse also depends on the general level of knowledge and skills of the programmer (those who are able to rise to the method level are inclined to reuse, and those who cannot rise above tactical planning usually avoid it).
If we restrict ourselves to the programmer's activities, then, first of all, we need to point out two aspects of this style: the use of existing components and the development of reusable components.
The use of reusable components is characterized by the following style features:
When developing reused components, it is necessary to take into account that such development always requires additional costs, which are associated with the following requirements:
In the characteristics of both aspects of programming from reuse, there is no explicit mention of the specifics of traditional computational models . Therefore, they do not depend on the style in which the components are written , and to a large extent on the characteristics of the computing systems on which they are implemented 2 . But the components and the computation model serve as the foundation on which the reuse add-in is based. And the stability of the building and even its architecture significantly depend on the quality of the foundation.
Consider the previously presented styles in terms of their adaptability to the combination with the style of reuse.
Automata programming is clearly related to the notion of a set of states that is global for each program, and to use a program fragment in isolation from this set is, generally speaking, meaningless. This indicates the natural reuse framework for this style.
First, if a fragment can be selected as a black box, that is, we are only interested in the ratio between its input and output data, then it can be viewed at the program level as an independent data processing node and thus gains independence from the program states. In turn, this is precisely what makes it possible to use such a fragment as an independent processing node of another program - to reuse it. On this principle all library math functions are built, the realization of which quite often is not required even to know when using.
Conventional reuse units for any programming style are procedures. They are autonomously described, and if they turn out to be independent of the context shared with other components, then, in fact, they become the same black boxes that we just talked about.
Another possible case, when the part clearly understood by the programmer, of the states of the external program can be interpreted as a homomorphic image of the part (again, explicitly distinguished) of the component states (gray box). Then the component can even be modified, i.e. it can be reused as a fragment or template. However, the methods of establishing a homomorphism between states are that high-level superstructure above automaton programming, which has not yet been created, and therefore the programmer here is highly dependent on the quality of meaningful conceptual analysis.
Ironically, little is better suited to combining structured programming with the reuse style . The requirements for the structure of the information space of the task and for the coordination with it of other components of the program provide regulated links between subtasks, which means that the task of identifying independent components is facilitated (but does not disappear). In addition to fully self-reusable components, here you can indicate reuse, in which the new part provides the necessary part of the context (that is, the component and this part of the context are reused; see Modula-2 and Object Pascal language modules). Modules can be thought of as gray boxes for structured programming. The possibilities of modularization are rather well studied and correctly implemented in the languages mentioned above.
It should be noted that for the cases considered, the reuse task often enough requires modification of what is provided. Consider the simplest example that does not go beyond reusing a black box. The square root function is defined only for non-negative arguments. Question: Does the reusable subroutine need to check this function? On the one hand, it increases the reliability of programming, but on the other hand, it turns out to be redundant, when it is known (can be proved) that the argument is greater than zero. In the framework of traditional techniques, simple mechanisms for disabling checks cannot exist, since they all violate the black box principle. In imperative languages, such multivariate subroutines are traditionally interpreted as something rudely non-structural and even non-automatic, like deformity.
Multivariance as a form of gray boxes corresponds to the natural expansion of logic - the calculation of predicates with partially ordered quantifiers - and can be correctly added to imperative structural languages, and it is added to automatic programming languages naturally, and one only regrets the absence of such a possibility .
When using the probential style, the possibility of restructuring the reusable component under the situation is much higher due to the high-level principles of computation. However, there are no precise practical estimates of this yet. As a rule, large and meaningfully complete fragments of programs are reused, most often those that constitute the basic mechanisms that develop the model of language computation. The lack of experience in developing large production projects with a significant use of this style makes one speculate about it presumably.
But non-imperativeness is in its essence better adapted for reuse and, in particular, for pattern reuse, since ratios instead of orders are easier to automatically transform or even simply reinterpret in a different environment (lack of imperativeness is another factor that causes the almost complete reversibility of mathematical results) . It is not surprising, for example, that fact database in Prolog programs often become their common parts.
In event programming, a program decomposition is prescribed in advance: highlighting the levels of event generation and processing in it. Already it itself suggests the feasibility of a common generator for similar programs.We should not forget that the event-based mechanism or control with the help of priorities inevitably increases the autonomy of handlers: for example, they may not necessarily rely on a certain sequence of calls, and, as a result, their flexibility increases.
Today, the most debugged in terms of reuse are programs in object-oriented and functional style. The common reason for this is the flexible means of abstraction of the respective languages and the clear separation of interfaces from implementations. This increases the potential and real possibilities of reuse. At the same time, say, an object-oriented style is effective only for sufficiently large systems and thus inspires programmers to build large systems of classes and objects that are strongly interconnected. And this, in turn, makes technologize development. The technology is currently associated with the development of typical models of fragments of object-oriented systems. Design patterns are created (called patterns), which are prescribed to be used to minimize communication in the system,ensure its developability .
Designing for future reuse of results is possible at four levels.
Приведенный перечень упорядочен по степени значимости уровней для переиспользования. Наибольшая эффективность достигается, когда удается при проектировании выйти на уровень решений, но одновременно этот уровень является наиболее сложным и трудоемким для разработки.
В заключение отметим, что условия применения стиля от переиспользования в реальном программировании должны включать в себя оценку как экономических показателей, так и уровня квалификации исполнителей. А это уже функции менеджера проекта, который должен решать, ориентироваться ли на переиспользование (в обоих аспектах) или нет, и если да, то решить многие финансовые и организационные проблемы, выходящие за рамки собственно программирования.
Здесь необходима трезвая оценка уровня своих специалистов и организованности работ, поскольку на действительно выгодное для переиспользования решение способны, как правило, лишь высококвалифицированные специалисты в хорошей инфраструктуре .
Программирование от образцов - вариант стиля от переиспользования, часто игнорируемый специалистами, но тем не менее он является весьма распространенным (даже профессионалы им пользуются). Это - подход к разработке программ по заданным заранее шаблонам. Это - скорее, целый набор вырожденных стилей, каждый из которых выделяется в связи с тем, что в той или иной ситуации скрывается под понятием образец, фрейм, шаблон.
Не всякое переиспользование уместно считать программированием от образцов. Так, когда повторно используется фрагмент, который можно рассматривать как черный ящик, то ничего, кроме результатов вычислений фрагмента, не внедряется в новую программу. Следовательно, фрагмент не предписывает метода построения программы. Противоположная ситуация с переиспользованием уровня проектных решений. Эти решения диктуют, как будет построена программа. Шаблоны и каркасы, появившиеся в какой-либо разработке или построенные специально, становятся образцами для нового программного проекта. При этом совсем не обязательно, чтобы образец и конструируемая программа были бы написаны на одном языке. Напротив, можно извлечь определенную выгоду из того, что образец не привязывается к модели вычислений разрабатываемой программы. Если образец специально создается для данной программы, например, чтобы лучше понять решаемую задачу, то для него целесообразно выбирать язык повышенного уровня или другой модели вычислений и за счет этого иметь возможность быстрее реализовать пробную версию, макет и т. д. Подобные соображения мотивируют разновидность стиля программирования от образцов, получившую название prototyping (see below). In this case, it is clear that there is not reuse, but actually programming from the sample. But such an ideal case is the exception rather than the rule: most often it is difficult to draw the line between an independent style and technological methods of reuse.
You can specify the following typical cases of application programming from samples.
Использование технологического и технического фреймов демонстрирует возможность и особенности сочетания программирования отобразцов с другими стилями. Фрейм, как основа конструируемой программы, может быть разработан в каком угодно стиле (например, как событийная система), но он предписывает программисту правила, а иногда и стиль, в котором должны заполняться слоты. Когда сочетание таких разнородных стилей, которое без специальной методики и поддержки было бы неосуществимым, становится продуктивным, тогда можно с полным правом говорить о программировании от образцов как о самостоятельном стиле и о реализующей его методике либо методологии.
The programming style of the samples is characterized by the fact that the developer of the program when creating slots is not at all interested in how these components will be used - this has been decided in advance within the framework of this programming system. The context in which the components are immersed is all that provided by the template or frame, and therefore there is no question of directly specifying global actions or using global conditions. It is precisely due to the strict localization of everything the programmer deals with that in this case the productivity and quality of his work are achieved.