📜 ⬆️ ⬇️

Technique: Moving Functions Between Objects (M. Fowler refactoring)

The Beginning Code
Technique: Compilation methods

In the sequel, the refactoring technique of the book Refactoring. Improvement of the existing Martin Fowler code.

The syntax will be in C #, but the main thing is to understand the idea, and it can be used in any other programming language.

Moving functions between objects


  1. Moving a method ( create a new method with a similar body in a class that uses it more often ).
    The method should be in a class that better reflects its subject area.
    From
    class View { private Shop shop; private List<User> FilterUsers(List<User> users, decimal koef) { List<User> activeUsers = new List<User>(); foreach(User user in users) { if(shop.IsActiveUser(user, koef)) { activeUsers.Add(user); } } return activeUsers; } } class Shop { public bool IsActiveUser(User user, decimal koef) { decimal rate = 1; if(koef > 1000) { rate = koef * 1.75; } return user.Boss || user.HasDiscount() && rate > 10; } } class User { private bool boss; public bool Boss { get { return boss; } } public bool HasDiscount() { // some condition } } 

    To
     class View { private Shop shop; private List<User> FilterUsers(List<User> users, decimal koef) { List<User> activeUsers = new List<User>(); foreach(User user in users) { if(user.IsActiveUser(koef)) { activeUsers.Add(user); } } return activeUsers; } } class Shop { } class User { private bool boss; public bool Boss { get { return boss; } } public bool HasDiscount() { // some condition } public bool IsActiveUser(decimal koef) { decimal rate = 1; if(koef > 1000) { rate = koef * 1,75; } return Boss || HasDiscount() && rate > 10; } } 

  2. Moving a field ( create a new field in the target class ).
    The field should be in a class that better reflects its subject area.
    From
     class Shop { private decimal rate; private DiscountType discountType; public Shop(DiscountType discountType, decimal rate) { this.rate = rate; this.discountType = discountType; } public decimal Rate { get{ return rate; } set{ rate = value; } } public bool HasDiscount(DateTime from, DateTime to) { // some calculation of flag return discountType.IsValid(rate) && flag; } public decimal GetDiscount(decimal amount, bool fullSum) { // some calculation of koef return discountType.GetAmount(rate, fullSum) * koef; } } class DiscountType { public bool IsValid(decimal rate) { // some calculation using rate } public decimal GetAmount(decimal rate, bool fullSum) { // some calculation using rate } } 

    To
     class Shop { private DiscountType discountType; public Shop(DiscountType discountType) { this.discountType = discountType; } public bool HasDiscount(DateTime from, DateTime to) { // some calculation of flag return discountType.IsValid() && flag; } public decimal GetDiscount(decimal amount, bool fullSum) { // some calculation of koef return discountType.GetAmount() * koef; } } class DiscountType { private decimal rate; public DiscountType(decimal rate) { this.rate = rate; } public decimal Rate { get{ return rate; } set{ rate = value; } } public bool IsValid() { // some calculation using rate } public decimal GetAmount(bool fullSum) { // some calculation using rate } } 

  3. Select a class ( create a new class and move the fields and methods from the old class to the new one ).
    The class must contain its own data model and methods for working with it, otherwise it becomes a God object.
    From
     class User { private string name; private string street; private string city; public string Name { get{ return name; } set{ name = value; } } public string Street { get{ return street; } set{ city = value; } } public string City { get{ return city; } set{ city = value; } } public string GetAddressInfo() { return string.Format("{0} \ {1}", city, street); } } 

    To
     class User { private string name; private Address address; public string Name { get{ return name; } set{ name = value; } } public string GetAddressInfo() { return address.GetFullAddress(); } } class Address { private string city; private string street; public string Street { get{ return street; } set{ city = value; } } public string City { get{ return city; } set{ city = value; } } public string GetFullAddress() { return string.Format("Adress: {0} \ {1}", city, street); } } 

  4. Embed a class ( move all functions to another class and delete the original one ).
    The class performs too few functions.
    From
     class User { private string name; private Address address; public string Name { get{ return name; } set{ name = value; } } public Address GetAddress() { return address } } class Address { private string areaCode; private string country; public string AreaCode { get{ return areaCode; } set{ areaCode = value; } } public string Country { get{ return country; } set{ country = value; } } } 

    To
     class User { private string name; private string areaCode; private string country; public string AreaCode { get{ return areaCode; } set{ areaCode = value; } } public string Country { get{ return country; } set{ country = value; } } public string Name { get{ return name; } set{ name = value; } } } 

  5. Hiding delegation ( create methods that hide delegation ).
    Encapsulation of some parts of the system.
    From
     class View { private void Init() { // some code User manager = currentUser.Office.Manager; } } class User { private Department department; public Department Office { get{ return department; } set{ department = value; } } } class Department { private User manager; public Department(User manager) { this.manager = manager; } public User Manager { get{ return manager; } } } 

    To
     class View { private void Init() { // some code User manager = currentUser.Manager; } } class User { private Department department; public User Manager { get{ return department.Manager; } } } class Department { private User manager; public Department(User manager) { this.manager = manager; } public User Manager { get{ return manager; } } } 

  6. Removing the mediator ( force the client to contact the delegate directly ).
    The class is too busy with simple delegation.
    From
     class View { private void Init() { // some code User manager = currentUser.Manager; decimal rate = currentUser.Rate; } } class User { private Department department; private UserType userType; public User Manager { get{ return department.Manager; } } public decimal Rate { get{ return userType.Rate; } } } class Department { private User manager; public User Manager { get{ return manager; } } } class UserType { private decimal rate; public decimal Rate { get{ return rate; } } } 

    To
     class View { private void Init() { // some code User manager = currentUser.Office.Manager; decimal rate = currentUser.Type.Rate; } } class User { private Department department; private UserType userType; public Department Office { get{ return department; } } public UserType Type { get{ return userType; } } } class Department { private User manager; public User Manager { get{ return manager; } } } class UserType { private decimal rate; public decimal Rate { get{ return rate; } } } 

  7. Introduction of an external method ( add a method for the client, to which the server class is passed as the 1st parameter ).
    You must add a method, but there is no possibility of modifying the class.
    From
     class View { private string GetUserName() { // some code return "\"" + userName + "\""; } private string GetCompanyName() { // some code return "\"" + companyName + "\""; } } 

    To
     class View { private string GetUserName() { // some code return userName.InDoubleQuote(); } private string GetCompanyName() { // some code return companyName.InDoubleQuote(); } } static class StringExtension { public static string InDoubleQuote(this string str) { return "\"" + str + "\""; } } 

  8. Introduce a local extension ( create a wrapper for the class (or using inheritance) and add the necessary methods ).
    It is necessary to add methods, but there is no possibility to modify the class.
    From
     sealed class Account { public decimal CalculateSum() { // calculation summ return summ; } } 

    To
     class ImportantAccount { private Account account; public decimal CalculateMaxSum() { // calculation rate return account.CalculateSum() * rate; } } 


Conflicts of Interest (as part of Review).


As a rule, all disputes / disagreements arising between the reviewer and comitterrom can be divided into the following:

I hope to continue and further "Technique: Data Organization".

Source: https://habr.com/ru/post/174779/


All Articles