Refactoring

from Wikipedia, the free encyclopedia

Refactoring (also refactoring , refactoring or restructuring ) describes in software development the manual or automated structural improvement of source texts while maintaining the observable program behavior. The aim is to improve readability , comprehensibility, maintainability and expandability, with the aim of significantly reducing the effort required for error analysis and functional extensions.

Refactoring is a central part of agile software development . There is usually spoken of "continuous" refactoring or "uncompromising" refactoring. In agile software development, such as coding or module testing, refactoring is an integral part of the software development process and is not limited to specific times or phases.

Origin of the term

The term was first used in a paper by Ralph Johnson and William Opdyke in 1990 ( Refactoring: An aid in designing application frameworks and evolving object-oriented systems . In: Proceedings of Symposion on Object-Oriented Programming Emphasizing Practical Applications (SOOPPA), September 1990). Opdyke received his doctorate on the subject in 1992.

They developed the idea of ​​a software refactory, which should make it easier to redesign (refactoring) computer programs.

The incorrect translation of refactoring stems from a confusion with a frequently quoted analogy that was not originally part of the term: Refactoring is a way of modifying a program in such a way that hidden structures are revealed without changing the functionality. This, so the (incorrect) conclusion by analogy , corresponds to the procedure for factoring polynomials in mathematics .

method

Refactoring is mainly used for unsightly places in the code (see code smell ) . The source text of a computer program is redesigned, with the actual program function remaining unchanged. The redesign of the source code is usually carried out according to the following criteria:

  • Readability
  • Clarity
  • Comprehensibility
  • Expandability
  • Avoidance of redundancy
  • Testability

The aspects of refactoring are closely related to the resulting advantages. Refactoring has an analogue in mathematics in a procedure that is called algebraic transformation , in which the aim of the transformation is also better readability, comprehensibility and, if necessary, expandability (of the system of equations). For this reason, functional languages ​​( Lisp , Haskell , OCaml , Erlang, and so on) are much better suited to refactoring because they are based on a mathematical programming paradigm.

Refactoring is facilitated and supported by:

  • Unit tests that can be used as regression tests to prove that the behavior of the program has not changed under the same conditions and that refactoring has not inadvertently introduced errors,
  • Tools, in particular integrated development environments that offer support in carrying out refactorings,
  • functional programming languages ​​(among other things because you can check the correctness of the code in functional languages ​​using mathematical methods),
  • a programming language with a strict type system (e.g. in the OCaml programming language ), which rules out many errors in advance (at compile time) because it ensures that the signature (interface) remains the same, even if the structure ( Implementation) changes. This saves a lot of unit tests in advance (since it eliminates many sources of error).

Possible refactorings

The following are particularly common refactorings:

  • Change of a symbol name
  • Moving a symbol to another module, e.g. B. a method in another class
  • Splitting a module (e.g. package, class, method) into several smaller modules or merging smaller modules into a larger one.
  • In the broadest sense, also the reformatting of a source text, e.g. B. with a beautifier
  • In the case of changed business processes when displaying using the Unified Modeling Language UML , the program code can be changed using "refactoring". This creates a robust and stable system architecture, since confusing changes do not have to be initiated in the code.
  • Applying higher-order functions in functional programming languages
  • Outsourcing (refactors) the common abstract logic of several modules in functors. (Functors are parameterized modules that receive modules as parameters and deliver modules as results.)

advantages and disadvantages

advantages

Refactoring is used to improve the maintainability of the design in such a way that it becomes easier for the programmer to functionally expand the existing code or to reuse it elsewhere . One tries to achieve this by improving the code in particular with regard to the following criteria:

  • Readability so that as many programmers as possible understand what the code is actually doing
  • Modularity and redundancy so that specific problem solutions can be used elsewhere and are not implemented multiple times
  • Coupling and cohesion so that future changes only have local effects
  • Testability (see unit test ) , so that it is possible to ensure the correct functioning of the code for the future by means of regression tests

In the usual software development cycle, there is a continuous cycle of specification , design , implementation and testing . After each run, the software product can enter this cycle again and again. With the classic techniques, however, this meant that after a change to the specification or a redesign , parts or even the entire program often had to be completely rewritten. Refactoring allows the developer to run this cycle permanently on a small scale and thus to continuously improve his product.

disadvantage

On the other hand, there are some disadvantages or complications, especially for the company and its environment:

  • Possible time consumption without real benefit for the customer, which also distracts from the "important tasks".
  • Refactoring can cause new, unexpected errors.
  • Interpretation and testing effort.
  • A question of taste, what the program structure looks like.
  • Coordination effort between programmers too high and this may lead to errors that affect the time.

The disadvantages mentioned can vary and are not part of refactoring.

Risks and their handling

Refactoring is only performed on working code (whose functionality should be preserved). But this also includes the risk of undesired changes and errors. To avoid (or at least minimize) this risk, various rules are used that make the process of refactoring less dangerous.

First, you should have a number of automatically running unit tests . These are applied before refactoring, and you won't start until the tests all work. In addition, a suitable program should be used to determine the test coverage and check whether the point in the code to be changed is actually protected by automated tests. This ensures that the program runs properly. After executing the refactoring, the test suite is executed again. This way you can immediately spot some errors in refactoring. However, it would be wrong to say that unit tests could make refactoring safe, unit tests only reduce the risks of refactoring.

The principle of small changes continues to apply. If you only change a little, you can hope to destroy only a little if you enter errors through the refactoring (nevertheless, small causes can have major effects). On the other hand, mistakes made can then also be found more easily. Most of the time, you can break down complex refactorings you are planning into simple small units. Before and after each step the integrity of the system is checked again by the tests . By using automated refactoring functions (such as those made available by Eclipse or Borland Delphi from version 2005), sources of error can also be effectively excluded and your own workload can be minimized.

Finally, there is a catalog of refactoring patterns that are used in a manner similar to the design patterns to avoid mistakes. A number of parameters are defined in each pattern. First there is the goal of the pattern ( extract method , rename class , etc.) and then a series of work instructions that have to be carried out for this action. Many of these patterns can now be implemented automatically by tools . As a software developer, you only have to decide which pattern to apply to what in order to improve the source code . It should be noted, however, that the mechanisms are often still quite error-prone. In the best case scenario, errors caused in this way lead to a problem during compilation, but runtime errors can also result. Extensive testing that is as automated as possible is therefore always required after refactoring.

example

This pre-refactoring Java code contains a temporary variable that is used for several purposes and has a meaningless name:

    double x = 2 * (breite + hoehe);
    System.out.println("Umfang: " + x);
    x = breite * hoehe;
    System.out.println("Fläche: " + x);

Refactoring declares a separate variable for each of the intended uses, each with a meaningful name:

    double umfang = 2 * (breite + hoehe);
    System.out.println("Umfang: " + umfang);
    double flaeche = breite * hoehe;
    System.out.println("Fläche: " + flaeche);

The two local variables can be eliminated by further refactoring.

Disadvantage:

  • The meaning of the expressions becomes less clear.
  • Expressions are harder to display in the debugger.

The resulting code gets neither better nor worse, since compilers have mastered common subexpression elimination as well as live variable analysis since the mid-1990s .

    System.out.println("Umfang: " + (2 * (breite + hoehe)));
    System.out.println("Fläche: " + (breite * hoehe));

You could also move the calculation to a class and use this:

    Rechteck rechteck = new Rechteck(breite, hoehe);
    System.out.println("Umfang: "   + rechteck.umfang() );
    System.out.println("Fläche: "   + rechteck.flaeche() );
    System.out.println("Eckenanzahl: " + rechteck.ecken() );
    System.out.println("Diagonalen: " + rechteck.diagonale(0,1) );

literature

  • Martin Fowler : Refactoring. How to improve the design of existing software . Addison-Wesley Verlag, Munich 2000, ISBN 3-8273-1630-8 . 2nd edition Refactoring: Improving the Design of Existing Code , Addison-Wesley 2018, ISBN 978-0-13-475759-9 .
  • Robert C. Martin: Clean Code: Refactoring, Patterns, Testing, and Clean Code Techniques . mitp, Frechen 2009, ISBN 978-3-8266-5548-7 .
  • William C. Wake: Refactoring Workbook . ISBN 0-321-10929-5 .
  • Ch. Bommer, M. Spindler, V. Barr: Software maintenance - basics, management and maintenance techniques . dpunkt.verlag, Heidelberg 2008, ISBN 3-89864-482-0
  • Joshua Kerievsky: Refactoring to Patterns (=  Programmer's Choice ). 1st edition. Addison-Wesley, 2006, ISBN 978-3-8273-2503-7 (English, industriallogic.com [accessed March 14, 2013] Original title: Refactoring to Patterns .).
  • William G. Grisworld, William F. Opdyke: The Birth of Refactoring: A Retrospective on the Nature of High-Impact Software Engineering Research in: IEEE Software Vol. 32 (6), November / December 2015 ( IEEE Computer Society Digital Library ).

Web links

Tools

Individual evidence

  1. ^ Floyd Marinescu, Abel Avram: Domain-Driven Design Quickly . Ed .: C4Media. InfoQ, 2007, ISBN 978-1-4116-0925-9 , pp. 57–59 (English, infoq.com [accessed March 7, 2013]).
  2. ^ Refactor Mercilessly. Portland Pattern Repository , accessed March 7, 2013 .