Refactoring techniques. Moving methods between classes

Lecture



If you have placed the functionality of the classes is not the best way - this is not a reason to despair.

Refactoring this group shows how to safely move functionality from one class to another, create new classes, and also hide implementation details from public access.

Moving method

Problem: The method is used in another class more than in its own.

Solution: Create a new method in the class that uses it more than others, and transfer the code from the old method there. Turn the original method code into a call to a new method in another class or remove it altogether.

Moving field

Problem: The field is used in another class more than in its own.

Solution: Create a field in the new class and redirect all users of the old field to it.

Class extraction

Problem: One class works for two.

Solution: Create a new class, move to it the fields and methods that are responsible for certain functionality.

Class embedding

Problem: The class does almost nothing, is not responsible for anything, and no responsibility is planned for this class.

Solution: Move all features from the described class to another.

Delegation Concealment

Problem: The client receives object B from the field or method of object A. Then the client calls some method of object B.

Solution: Create a new method in class A, which would delegate the call to object B. Thus, the client will no longer know about class B and depend on it.

Removing a reseller

Problem: The class has too many methods that simply delegate work to other objects.

Solution: Remove these methods and force the client to call the final methods directly.

Introduction of the external method

Problem: The utility class does not contain the method you need, and you cannot add a method to this class.

Solution: Add a method to the client class and pass in it a utility class object as an argument.

Introduction of local extension

Problem: The utility class lacks some methods that you need. You cannot add them to this class.

Solution: Create a new class that would contain these methods, and make it a successor of the service class, or its wrapper.

same known as: Move Method

Refactoring Moving Method

Problem

The method is used in another class more than in its own.

Decision

Create a new method in the class that uses it more than others, and transfer the code from the old method there. Turn the original method code into a call to a new method in another class or remove it altogether.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

  1. You want to move a method to a class that contains data with which this method basically works. This increases connectivity within classes .

  2. You want to move a method to remove or reduce the dependence of the class that calls this method on the class in which it was located. This can be useful if the calling class already has a dependency on the class where you plan to transfer the method. Thus, you reduce connectivity between classes .

Refactoring order

  1. Check out all the features used by the old method in its class. Perhaps they should also be moved. Follow this rule - if the feature is used only by the method you are interested in, it should be transferred accurately. If the feature is used by other methods, it may be necessary to transfer these methods. Sometimes it is much easier to move a pack of methods than to configure the interaction between them in different classes.

    Check if the method is defined in the superclasses and subclasses of the receiver. If so, you will either have to abandon the idea of ​​transfer, or implement a kind of polymorphism in the recipient class to provide different functionality of the method, which was divided into donor classes.

  2. Declare a new method in a class receiver. Perhaps you should come up with a new name for the method, which in the new class will suit it more.

  3. Determine how you will contact the class of the recipient. It is quite possible that you already have a field or method that returns a suitable object, but if not, you will need to write a new method or field in which the object of the class of the receiver would be stored.

    Now you have a way to access the receiver object and a new method in its class. With this all you can already turn the old method into a call to the new method.

  4. Rate, is it possible to remove the old method at all? In this case, it will be necessary in all the places where this method is used, to put an appeal to the new.

Also known as: Move Field

Refactoring Moving Fields

Problem

The field is used in another class more than in its own.

Decision

Create a field in the new class and redirect all users of the old field to it.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

Often, fields are carried as part of extracting one class from another. It is not easy to decide which class should remain in the field. Nevertheless, we have a good recipe - the field should be where the methods are located that use it (or where there are more of these methods).

This rule will help you in other cases where the field is simply not where it is needed.

Refactoring order

  1. If the field is public, it will be much easier for you to refactor if you make it private and provide public access methods (you can use refactoring to encapsulate the field).

  2. Create the same field with access methods in the target class.

  3. Determine how you will contact the class of the recipient. It is possible that you already have a field or method that returns a suitable object. If not, you will need to write a new method or field in which the object of the class of the recipient would be stored.

  4. Replace all references to the old field with the corresponding method calls in the class of the receiver. If the field is not private, do it in the superclass and in the subclasses.

  5. Delete the field in the source class.

Related refactorings

Moving field

Helps refactoring

Class extraction

Class embedding

Fights odor

Shot Shot

Parallel inheritance hierarchies

Inappropriate intimacy

Also known as: Extract Class

Refactoring Class Extraction

Problem

One class works for two.

Decision

Create a new class, move to it the fields and methods that are responsible for a certain functionality.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

Classes always look sharp and clear from the start. They do their job and do not climb into the duties of other classes. However, over the course of the life of the program, one method is added — here, one field — there. As a result, some classes get a ton of extra responsibilities.

Merits

  • This refactoring is intended to assist in adhering to the principle of the sole duty of the class . This makes the code for your classes clearer and clearer.

  • Classes with the only responsibility are more reliable and resistant to change. For example, you have a class responsible for ten different things. And when you have to make changes to it, you risk breaking other things when adjusting one thing.

disadvantages

  • If you overdo it during this refactoring, you will have to resort to embedding the class.

Refactoring order

Before starting refactoring, be sure to determine exactly how the class should be divided.

  1. Create a new class that will contain the selected functionality.

  2. Create a connection between the old and the new class. Best of all, if this connection is one-way; while the second class can be reused without problems. On the other hand, if you think that this is necessary, you can always create two-way communication.

  3. Use the moving field and moving method for each field and method that you choose to move to the new class. For methods, it makes sense to start from private, so you reduce the likelihood of making a lot of mistakes. Try to move a little and test the result after each move, it will save you from having to correct a large number of errors at the very end.

    Once done with the move, look again at the resulting classes. Perhaps the old class now makes sense to call it differently because of its changed responsibilities. Check again whether it is possible to get rid of two-way communication between classes, if it has arisen.

  4. Another nuance is the availability of a new class from the outside. You can completely hide it from the client, making it private, while managing its fields from the old class. Or make it public by giving the customer the opportunity to directly change values. The decision depends on how safe for the behavior of the old class will be unexpected direct changes of values ​​in the new class.

Anti-refactoring

Class embedding

Related refactorings

Extract subclass

Replacing a simple field with an object

Fights odor

Code duplication

Big class

Divergent modifications

Data groups

Obsession with elementary types

Temporary field

Inappropriate intimacy

Also Known As: Inline Class

Refactoring Class Embedding

Problem

The class does almost nothing, is not responsible for anything, and no responsibility is planned for this class.

Decision

Move all features from the described class to another.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

Often, this refactoring is the result of the recent "relocation" of part of the class features to others, after which little remains of the original class.

Merits

  • Less useless classes - more free RAM, including in your head.

Refactoring order

  1. Create in the class-receiver public fields and methods that are in the class-donor. Methods should refer to similar methods of the donor class.

  2. Replace all references to the donor class with references to the fields and methods of the receiver class.

  3. It's time to test the program and make sure that no errors were made during the work. If the tests show that everything works as it should, we begin to use the method transfer and the field transfer in order to completely move all the functionality to the target class from the original class. We continue to do this until the original class is completely empty.

  4. Remove the original class.

Anti-refactoring

Class extraction

Fights odor

Shot Shot

Lazy class

Theoretical community

Also known as: Hide Delegate

Refactoring Delegation Hiding

Problem

The client receives object B from the field or method of object A. The client then calls some method of object B.

Decision

Create a new method in class A, which would delegate the call to object B. Thus, the client will no longer know about class B and depend on it.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

First you need to decide on the names:

  • A server is an object that the client has direct access to.

  • A delegate is the final object that contains the functionality that the client needs.

A call chain appears when a client requests one object from another, then the second object requests another, and so on. Such call sequences mean that the client is connected with navigating through the class structure. Any changes to intermediate links imply the need to modify the client.

Merits

  • Hides delegation from the client. The less client code knows the details about the relationships between objects, the easier it will be later to make changes to the program.

disadvantages

  • If it is necessary to create too many delegating methods, the class-server risks becoming an unnecessary intermediate and leading to a pack of middleman.

Refactoring order

  1. For each delegate class method called by the client, you need to create a method in the server class that delegates the call to the delegate class .

  2. Modify the client code so that it calls the server class methods.

  3. If, after all changes, the client no longer needs the delegate class , you can remove the delegate class access method from the server class (the method that was used initially to get the delegate class ).

Anti-refactoring

Deleting a reseller

Fights odor

Call chain

Inappropriate intimacy

Also known as: Move Field

Refactoring Moving Fields

Problem

The field is used in another class more than in its own.

Decision

Create a field in the new class and redirect all users of the old field to it.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

Often, fields are carried as part of extracting one class from another. It is not easy to decide which class should remain in the field. Nevertheless, we have a good recipe - the field should be where the methods are located that use it (or where there are more of these methods).

This rule will help you in other cases where the field is simply not where it is needed.

Refactoring order

  1. If the field is public, it will be much easier for you to refactor if you make it private and provide public access methods (you can use refactoring to encapsulate the field).

  2. Create the same field with access methods in the target class.

  3. Determine how you will contact the class of the recipient. It is possible that you already have a field or method that returns a suitable object. If not, you will need to write a new method or field in which the object of the class of the recipient would be stored.

  4. Replace all references to the old field with the corresponding method calls in the class of the receiver. If the field is not private, do it in the superclass and in the subclasses.

  5. Delete the field in the source class.

Related refactorings

Moving field

Helps refactoring

Class extraction

Class embedding

Fights odor

Shot Shot

Parallel inheritance hierarchies

Inappropriate intimacy

Also known as: Remove Middle Man

Refactoring Removing a mediator

Problem

The class has too many methods that simply delegate work to other objects.

Decision

Remove these methods and force the client to call the final methods directly.

  Refactoring techniques.  Moving methods between classes

  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

In this refactoring we will use the names from the concealment of the delegation, namely:

  • A server is an object that the client has direct access to.

  • A delegate is the final object that contains the functionality that the client needs.

There are two kinds of problems:

  1. The class server does nothing on its own, creating useless complexity. In this case, it is worth considering whether this class is needed at all.

  2. Every time a new feature appears in the delegate , you need to create a delegating method for it in the server class . This is expensive with a large number of changes.

Refactoring order

  1. Create a getter to access the delegate class object from the server class object.

  2. Replace calls to the delegating methods of the server class with direct calls to the methods of the delegate class .

Anti-refactoring

Delegation Concealment

Fights odor

Mediator

Also known as: Introduce Foreign Method

Refactoring Introducing External Method

Problem

The utility class does not contain the method you need, and you cannot add a method to this class.

Decision

Add a method to the client class and pass an object of the utility class to it as an argument.

 class Report {
   // ...
   void sendReport () {
     Date nextDay = new Date (previousEnd.getYear (),
       previousEnd.getMonth (), previousEnd.getDate () + 1);
     // ...
   }
 } 
 class Report {
   // ...
   void sendReport () {
     Date newStart = nextDay (previousEnd);
     // ...
   }
   private static Date nextDay (Date arg) {
     return new Date (arg.getYear (), arg.getMonth (), arg.getDate () + 1);
   }
 } 
 class Report 
 {
   // ...
   void SendReport () 
   {
     DateTime nextDay = previousEnd.AddDays (1);
     // ...
   }
 } 
 class Report 
 {
   // ...
   void SendReport () 
   {
     DateTime nextDay = NextDay (previousEnd);
     // ...
   }
   private static DateTime NextDay (DateTime date) 
   {
     return date.AddDays (1);
   }
 } 
 class Report {
   // ...
   public function sendReport () {
     $ previousDate = clone $ this-> previousDate;
     $ paymentDate = $ previousDate-> modify ("+ 7 days");
     // ...
   }
 } 
 class Report {
   // ...
   public function sendReport () {
     $ paymentDate = self :: nextWeek ($ this-> previousDate);
     // ...
   }
   / **
    * Foreign method.  Should be in Date.
    * /
   private static function nextWeek (DateTime $ arg) {
     $ previousDate = clone $ arg;
     return $ previousDate-> modify ("+ 7 days");
   }
 } 
 class Report:
     # ...
     def sendReport (self):
         nextDay = Date (self.previousEnd.getYear (),
             self.previousEnd.getMonth (), self.previousEnd.getDate () + 1);
         # ... 
 class Report:
     # ...
     def sendReport (self):
         newStart = self._nextDay (self.previousEnd)
         # ...
        
     def _nextDay (self, arg):
         return Date (arg.getYear (), arg.getMonth (), arg.getDate () + 1) 
 class Report {
   // ...
   sendReport (): void {
     let nextDay: Date = new Date (previousEnd.getYear (),
       previousEnd.getMonth (), previousEnd.getDate () + 1);
     // ...
   }
 } 
 class Report {
   // ...
   sendReport () {
     let newStart: Date = nextDay (previousEnd);
     // ...
   }
   private static nextDay (arg: Date): Date {
     return new Date (arg.getFullYear (), arg.getMonth (), arg.getDate () + 1);
   }
 } 

Reasons for refactoring

You have code that uses data and methods of a particular class. You come to the conclusion that this code will look and work much better inside the new method in this class. However, you have no opportunity to add such a method to a class (for example, because the class is in a third-party library).

This refactoring is especially beneficial in cases where a section of code that you want to transfer to a method is repeated several times in different places in the program.

Since you pass a utility class object to the parameters of a new method, you have access to all of its fields. Inside this method, you can do almost everything you need, as if the method were part of a service class.

Merits

  • Removes duplicate code. If your code section is repeated in several places, you can replace them with a method call. This is more convenient than duplication, even taking into account the fact that the external method is not where we would like.

disadvantages

  • Причины того, почему метод служебного класса находится в клиентском классе, не всегда очевидны для того специалиста, который будет поддерживать код после вас. Если данный метод может быть использован и в других классах, имеет смысл создать обёртку над служебным классом, и поместить метод туда. То же самое имеет смысл сделать, если таких служебных методов несколько. В этом поможет рефакторинг введение локального расширения.

Refactoring order

  1. Создайте новый метод в клиентском классе.

  2. В этом методе создайте параметр, в который будет передаваться объект служебного класса. Если этот объект может быть получен из клиентского класса, параметр можно не создавать.

  3. Извлеките волнующие вас участки кода в этот метод и замените их вызовами метода.

  4. Обязательно оставьте в комментарии к этому методу метку Foreign method и призыв поместить этот метод в служебный класс, если такая возможность появится в дальнейшем. Это облегчит понимание того, почему этот метод находится в данном классе для тех, кто будет поддерживать программный продукт в будущем.

Родственные рефакторинги

Введение локального расширения

Вынести все расширенные методы в отдельный класс обёртку/наследник служебного класса.

Борется с запахом

Неполнота библиотечного класса

Также известен как: Introduce Local Extension

Рефакторинг Введение локального расширения

Problem

В служебном классе отсутствуют некоторые методы, которые вам нужны. При этом добавить их в этот класс вы не можете.

Decision

Create a new class that would contain these methods, and make it a successor of the service class, or its wrapper.

  Refactoring techniques.  Moving methods between classes
  Refactoring techniques.  Moving methods between classes

Reasons for refactoring

There is no method you need in the class you are using. Or even worse, you cannot add them there (for example, because the classes are in a third-party library). You have two options:

  • Create a subclass of the class of interest, which will contain new methods, and inherit everything else from the parent class. This path is easier, but sometimes it is blocked in the utility class itself with the help of the final directive.

  • Create a wrapper class that will contain all the new methods, and delegate the rest to the associated object of the service class. This path is more laborious, since you will need to have in the appendage not only the code for maintaining communication between the wrapper and the service object, but also a large number of simple delegating methods that will emulate the public interface of the service class.

Merits

  • By placing additional methods in a separate extension class (wrapper or subclass), you do not clutter client classes with code that does not belong to them. This increases the connectivity of the program components and the possibility of their reuse.

Refactoring order

  1. Create a new extension class:

    • or make it a successor to the service class;

    • or, if you decide to make a wrapper, create a field in it to store the object of the utility class to which delegation will occur. In this case, you will need to create methods that repeat the public methods of the service class and contain a simple delegation to the methods of the service object.

  2. Create a constructor that uses the parameters of the utility class constructor.

  3. In addition, create an alternative "converting" constructor that accepts only the object of the original class in the parameters. This will help in substituting the extension instead of the objects of the original class.

  4. Create new advanced methods in the class. Move external methods from other classes into it, or delete them if the extension already has such functionality.

  5. Replace the use of the utility class with a new extension class in those places where you need extended functionality.


Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Refactoring theory

Terms: Refactoring theory