Рефакторинг иерархии классов Java без изменения вызывающего кода, возможно ли это? - PullRequest
0 голосов
/ 20 декабря 2018

Текущая ситуация:

в системе Java, у нас есть класс с именем Passenger, как показано ниже, скажем,

public Class Passenger{
//lots of member fields
private String firstName ;
private String lastName ;
private WhatEverAttribute att;

//lots of getters & setters
WhatEverAttribute getAtt(){ return att;}
void setAtt(WhatEverAttribute attIn){ this.att=attIn;}
...

//and lots of methods,for example
List<String> doWhatEverFuction(...){ return ... }
...
}

И в приложении в другом месте есть много мест, которые будут создаватьи используйте этот класс как:

Passenger p1 = new Passenger();
p.setFirstName("blablabla")
p.setAtt(xxx);
Passenger p2 = new Passenger();
p2.setAtt(yyy)
List retl = p2.doWhatEverFuction(...);
...

Система, которая ранее управляла только пассажирами воздушных / полетных рейсов, поэтому класс «Пассажир» фактически является моделью данных для авиапассажиров,

Теперь проблема в том, что нам нужноЧтобы расширить модель и создать иерархию, поскольку Пассажир будет общей моделью Пассажира, содержащей общие поля и функции, новая модель AirPassenger и SeaPassenger расширят ее: введите описание изображения здесь

Таким образом, некоторые общие поля и функции будут сохранены в Passenger для совместного использования между AirPassenger и SeaPassenger, но большинство специфических полей и функций для авиапассажиров будут перенесены в AirPassenger,

, тогда все будут знать, что я должен изменить существующий коддоступ к пассажиру с

   Passenger p = new Passenger();
   p.xxxxxx();

до

   AirPassenger p = new AirPassenger();
   p.xxxxxx();  

Есть так многомест, и я не хочу вручную изменять их во многих местах в существующем коде, обращающемся к Passenger из всего приложения,

Что я хочу, так это после создания иерархии, остальная часть кода все еще работает без каких-либо изменений,используя некоторые приемы техники, я мог бы вернуть AirPassenger через новый конструктор Passenger (), например:

Passenger{

Passenger(){

return Passenger("Air")

}

Passenger(String type){
  Switch(type){
  ...
  case "Air": return new AirPassenger();
  ...
  }

}

}

, с помощью некоторых динамических функций Java, CGLIB или чего-либо еще, возможно ли это?

Ответы [ 2 ]

0 голосов
/ 21 декабря 2018

Конечно, я могу найти и заменить, и большинство IDE может это сделать, но проблема в том, что в системе есть какой-то файл конфигурации, и txt-файл, работает как словарь и шаблоны, которые вы знаете, они используют слово Пассажира или добавляютпрефикс и суффикс или строчные буквы, вы знаете, вы не можете просто найти и заменить, так что не нужно вносить изменения только в код Java, поэтому я бы не стал их менять, если есть способ, это мой вопрос.

//you create an instance of class
ClassA obj = new ClassA();
//you call method doIT() inside ClassA
obj.doIt();

Теперь мне нужно перемоделировать его, так как ClassA становится суперклассом, и эти методы проталкиваются в его подкласс, скажем, ClassB, поэтому ClassB расширяет ClassA, метод doIt () теперь находится внутри ClassB, все знают, как обычно, мне нужно изменить приведенный выше код на

//you create an instance of ClassB, 
ClassB obj = new ClassB(); 
//or ClassB obj = new ClassA(); you have little code in the ClassA constuctor, etc.
//then you call a method
obj.doIt();

Я знаю это, но мой вопрос , без внесения изменений

ClassA obj = new ClassA();
obj.doIt();

Есть ли какие-нибудь хитрости, которые заставят его работать с новой моделью?

0 голосов
/ 20 декабря 2018

Подход, который вы выберете, будет зависеть от многих факторов, в основном от того, как и где используется ваш код.Если вам доступны все виды использования этого класса, я настоятельно рекомендую вам не использовать предложенное решение - правильная иерархия наследования поможет сохранить ваш код организованным и позволит легко расширить вашу программу в будущем.Многие IDE (например, IntelliJ) предлагают замечательную функциональность для интеллектуального рефакторинга вашего кода и извлечения в новые классы, которые будут выполнять почти всю работу автоматически и обеспечивать выполнение кода в соответствии с назначением.

Если возможно, ваша ситуация - это классический случай использования абстрактного родительского класса.Поскольку не существует такой вещи, как «обычный пассажир», более правильным вариантом для вашей иерархии было бы объявление пассажира как абстрактного класса.Таким образом, экземпляры Passenger не могут быть созданы напрямую, но сам класс может содержать реализации, которые наследующие классы могут использовать или переопределять по мере необходимости.Вы также можете добавить фабричный метод к этому классу, который будет возвращать нового Пассажира правильного типа в соответствии с вводом (как вы предложили).

Редактировать:

Полиморфизм и иерархия классов работают в Java очень особым образом.То есть некоторые ограничения накладываются на программиста для поддержания читабельности и модульности кода.То, о чем вы просили (в ваших поясняющих комментариях), невозможно описать.

Вы создаете два новых класса:

class AirPassenger extends Passenger {

    ...

    public void doSomethingAir() {
    ...
    }

    ...
}

class SeaPassenger extends Passenger {
    ...

    public void doSomethingSea() {
    ...
    }

    ...  
}

Вы преобразовываете некоторые из его методов вновый класс AirPassenger и оставить там, где они есть.Первое, что вы заметите, это то, что вы не можете использовать существующий конструктор Passenger для возврата экземпляров AirPassenger или SeaPassenger, поскольку конструктор является пустым методом и не имеет возвращаемого значения.Поэтому вам нужно будет предоставить метод конструирования Factory для создания экземпляров AirPassenger или SeaPassenger.

    Passenger createPassenger(String passengerType) { 
        switch (passengerType) {
            case "sea":
                return new SeaPassenger();
            default:
                return new AirPassenger();
        }
    }

Вы переделали методы из Пассажира в AirPassenger.Эти методы больше не существуют в классе Passenger, и они не могут быть вызваны объектами Passenger.Однако вы можете использовать явное приведение типов для повторного приведения всех объектов Пассажира в AirPassenger, и тогда вы сможете использовать все методы, которые теперь есть в AirPassenger.Это также можно сделать одним способом:

AirPassenger convertToAirPassenger(Passenger passenger) {
    return (AirPassenger) passenger;
}
...