У меня есть интерфейс Java и реализация классов, которые нуждаются в разных аргументах при вызове подобного поведения. Какое из следующего в основном подходит?Методы интерфейса с переменными типами аргументов
В первом варианте у меня разные классы наследуют общее поведение от базового интерфейса, и все различия реализуются непосредственно в классах, а не в интерфейсе. Это кажется наиболее подходящим, но я должен сделать ручной ввод текста в коде.
public class VaryParam1 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
static List<Car> carsList = new ArrayList<>();
static List<TruckWithTrailer> trucksList = new ArrayList<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
//violates LSP?
((Car)list.get(VehicleType.WITHOUT_TRAILER)).paint(1); //ok - but needed manual cast
((TruckWithTrailer)list.get(VehicleType.WITH_TRAILER)).paint(1, "1"); //ok - but needed manual cast
carsList.add(new Car());
trucksList.add(new TruckWithTrailer());
//Does not violate LSP
carsList.get(0).paint(1);
trucksList.get(0).paint(1, "1");
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
interface Vehicle{
//definition of all common methods
void drive();
void stop();
}
class Car implements Vehicle {
public void paint(int vehicleColor) {
System.out.println(vehicleColor);
}
@Override
public void drive() {}
@Override
public void stop() {}
}
class TruckWithTrailer implements Vehicle {
public void paint(int vehicleColor, String trailerColor) {
System.out.println(vehicleColor + trailerColor);
}
@Override
public void drive() {}
@Override
public void stop() {}
}
Во втором варианте я переместил методы, один уровень до интерфейса, но теперь мне нужно реализовать поведение с UnsupportedOpException. Это похоже на запах кода. В коде мне не нужно делать ручное кастинг, но у меня также есть возможность вызывать методы, которые будут вызывать исключение во время выполнения - без проверки времени компиляции. Это не такая уж большая проблема - только эти методы с исключением, которые выглядят как запах кода. Является ли этот способ внедрения лучшей практикой?
public class VaryParam2 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
list.get(VehicleType.WITHOUT_TRAILER).paint(1); //works
list.get(VehicleType.WITH_TRAILER).paint(1, "1"); //works
list.get(VehicleType.WITHOUT_TRAILER).paint(1, "1"); //ok - exception - passing trailer when no trailer - no compile time check!
list.get(VehicleType.WITH_TRAILER).paint(1); //ok - exception - calling trailer without trailer args - no compile time check!
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
interface Vehicle{
void paint(int vehicleColor);
void paint(int vehicleColor, String trailerColor); //code smell - not valid for all vehicles??
}
class Car implements Vehicle {
@Override
public void paint(int vehicleColor) {
System.out.println(vehicleColor);
}
@Override
public void paint(int vehicleColor, String trailerColor) { //code smell ??
throw new UnsupportedOperationException("Car has no trailer");
}
}
class TruckWithTrailer implements Vehicle {
@Override
public void paint(int vehicleColor) { //code smell ??
throw new UnsupportedOperationException("What to do with the trailer?");
}
@Override
public void paint(int vehicleColor, String trailerColor) {
System.out.println(vehicleColor + trailerColor);
}
}
Здесь я использовал дженерики для того, чтобы иметь общий метод в интерфейсе, и тип параметра определяется в каждом классе реализации. Проблема здесь в том, что у меня есть необработанные призывы рисовать. Это более похоже на проблему прямого кастинга в варианте 1. Bur здесь у меня также есть возможность вызывать методы, которые я не мог бы!
public class VaryParam3 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
list.get(VehicleType.WITHOUT_TRAILER).paint(new VehicleParam()); //works but unchecked call
list.get(VehicleType.WITH_TRAILER).paint(new TruckWithTrailerParam()); //works but unchecked call
list.get(VehicleType.WITHOUT_TRAILER).paint(new TruckWithTrailerParam()); //works but should not!
list.get(VehicleType.WITH_TRAILER).paint(new VehicleParam()); //ClassCastException in runtime - ok but no compile time check
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
class VehicleParam {
int vehicleColor;
}
class TruckWithTrailerParam extends VehicleParam {
String trailerColor;
}
interface Vehicle<T extends VehicleParam>{
void paint(T param);
}
class Car implements Vehicle<VehicleParam> {
@Override
public void paint(VehicleParam param) {
System.out.println(param.vehicleColor);
}
}
class TruckWithTrailer implements Vehicle<TruckWithTrailerParam> {
@Override
public void paint(TruckWithTrailerParam param) {
System.out.println(param.vehicleColor + param.trailerColor);
}
}
Так вот вопрос - какой из этих 3 вариантов является лучшим выбором (или, если есть какой-то другой вариант, я не нашел)? С точки зрения дальнейшей эксплуатации, изменение и т.д.
UPDATE
Я обновил этот вопрос, и теперь у меня есть метод рисования, который можно назвать только после того, как объект построен.
До сих пор это выглядит как самый лучший вариант, как это предлагается в следующей почте:
public class VaryParam4 {
static Map<VehicleType, Vehicle> list = new HashMap<>();
public static void main(String[] args) {
list.put(VehicleType.WITHOUT_TRAILER, new Car());
list.put(VehicleType.WITH_TRAILER, new TruckWithTrailer());
list.get(VehicleType.WITHOUT_TRAILER).paint(new PaintConfigObject()); //works but can pass trailerColor (even if null) that is not needed
list.get(VehicleType.WITH_TRAILER).paint(new PaintConfigObject()); //works
}
}
enum VehicleType {
WITHOUT_TRAILER,
WITH_TRAILER;
}
class PaintConfigObject {
int vehicleColor;
String trailerColor;
}
interface Vehicle{
void paint(PaintConfigObject param);
}
class Car implements Vehicle {
@Override
public void paint(PaintConfigObject param) {
//param.trailerColor will never be used here but it's passed in param
System.out.println(param.vehicleColor);
}
}
class TruckWithTrailer implements Vehicle {
@Override
public void paint(PaintConfigObject param) {
System.out.println(param.vehicleColor + param.trailerColor);
}
}
Почему количество дверей, имеющих отношение к * вождению * транспортное средство? Наверное, это что-то определено во время строительства? –
Я расскажу пример с помощью метода paint(), в котором автомобиль имеет краску (carcolor) и грузовик имеет боль (carcolor, trailercolor). Цвета определяются во время выполнения, после того, как транспортные средства уже сконструированы. – bojanv55
В первом примере вы подразумеваете, что вы уже знаете тип «Vehicle», в зависимости от значения перечисления, которое вы передаете. Это правильно? –