Абстрактные классы и полиморфизм - как обычно обрабатываются дополнительные методы и свойства? - PullRequest
2 голосов
/ 16 февраля 2020

Представьте себе компанию по прокату автомобилей, у которой есть машины для внутреннего пользования, а также грузовики для аренды. Автомобили используют газ, а грузовики - дизель. У грузовиков есть дополнительные вещи, которые они делают, которых нет у автомобилей - они сдаются в аренду. Итак, у меня есть следующий код:

abstract class Vehicle
{
    public abstract FuelType Fuel();
}

class Car : Vehicle
{
    public override FuelType Fuel()
    {
        return FuelType.Gas;
    }
}

class Truck : Vehicle
{
    public override FuelType Fuel()
    {
        return FuelType.Diesel;
    }

    //Not in base class or Car class
    public List<Rental> Rentals()
    {
        return new List<Rental>();
    }
}


class Rental
{
      //...some stuff here
}


enum FuelType
{
    Gas,
    Diesel
}

Что обычно делается, когда у дочерних классов есть дополнительные методы и свойства? Пример:

        List<Vehicle> vehicles = new List<Vehicle>() { };

        vehicles.Add(new Car());
        vehicles.Add(new Truck());

        foreach (var vehicle in vehicles)
        {
            Console.WriteLine(vehicle.Fuel().ToString());

            //Pseudocode here:
            if(vehicle.GetType() is Truck)
            {
                //Provide rental information
                Truck truck = (Truck)vehicle;
                truck.ProvideSomeInfo();
            }
        }

Я понимаю, как работает полиморфизм, когда у вас есть классы с одинаковыми методами, свойствами, полями и т. Д. c. Что обычно делается, когда вам нужно работать с базовым классом И вам нужно работать с дополнительными методами, полями, свойствами, которые не разделяются всеми дочерними элементами?

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

Ответы [ 2 ]

1 голос
/ 16 февраля 2020

Рекомендуется использовать интерфейсы.

class Truck : IVehicle, IRentable, ITrackable    //you can also inherit from abstract VehicleBase class

Затем:

foreach (var vehicle in vehicles)
    {
        Console.WriteLine(vehicle.Fuel().ToString());

        if(vehicle.GetType() is IRentable)
        {
            //Provide renting status
            Console.WriteLine((vehicle as IRentable)?.IsRented); //note I didn't cast for Truck directly
        }

        if(vehicle.GetType() is ITrackable)
        {
            //Provide position
            Console.WriteLine((vehicle as ITrackable)?.Position);
        }
    }

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

Еще одна полезная практика , если вы создайте один большой класс транспортных средств с большим количеством функций:

  • Размер
  • MainType с enum { Truck, PassengerCar }
  • WheelBase (8, 4 и др. c)
  • CanHaveMoreThan1Engine
  • FuelType с enum { Electric, Kerosene }
  • List<KeyValuePair<string, string> Двигатели (например, <"CommonRail4", "Diesel">)
  • CanProvideTrackingInfo
  • GetTrackingInfo()
0 голосов
/ 17 февраля 2020

в вашем случае, использование interface было бы более уместным.

вы можете определить свой контракт в своих интерфейсах, а также унаследовать несколько интерфейсов для каждого класса.

Интерфейс будет реализовывать методы, свойства в классе, а затем вы сможете настроить их логи c по мере необходимости. Когда вы инициализируете объект с помощью этого интерфейса, вы сможете использовать предоставляемые свойства или методы.

пример:

public enum VehicleMake
{
    Acura,
    Audi,
    BMW,
    Chevrolet,
    Dodge,
    Fait,
    Ford,
    Hyundai,
    Honda,
    Nissan,
    Toyota
}

public enum FuelType
{
    Gas,
    Diesel
}

public interface IVehicle
{
    FuelType Fuel { get; }

    VehicleMake Make { get; set; }

    string Model { get; set; }

    int MakeYear { get; set; }

    IRental Rental { get; set; }
}

public interface IRental
{
    bool IsRented { get; set; }
}

public class Rental : IRental
{
    public bool IsRented { get; set; }
}

public class Car : IVehicle
{
    public FuelType Fuel => FuelType.Gas;

    public VehicleMake Make { get; set; }

    public string Model { get; set; }

    public int MakeYear { get; set; }

    public IRental Rental { get; set; }

}

public class Truck : IVehicle
{
    public FuelType Fuel => FuelType.Diesel;

    public VehicleMake Make { get; set; }

    public string Model { get; set; }

    public int MakeYear { get; set; }

    public IRental Rental { get; set; }

}

Это даст нам следующее:

var vehicles = new List<IVehicle>
{
    new Car
    {
        Make = VehicleMake.BMW,
        MakeYear = 2020,
        Rental = new Rental { IsRented = true }
    },
    new Car
    {
        Make = VehicleMake.Toyota,
        MakeYear = 2000,
        Rental = new Rental { IsRented = false }
    },
    new Truck
    {
        Make = VehicleMake.Nissan,
        MakeYear = 2015,
        Rental = new Rental { IsRented = false }
    }
};


foreach (var vehicle in vehicles)
{
    Console.Write($"{vehicle.GetType().Name}\t");
    Console.Write($"{vehicle.Make.ToString()}\t");
    Console.Write($"{vehicle.MakeYear.ToString()}\t");
    Console.Write($"{vehicle.Fuel.ToString()}\t");    

    if (vehicle.Rental?.IsRented == true)
    {
        Console.Write("rented");
    }
    else
    {
        Console.Write("not rented");
    }

    Console.WriteLine();
}

Итак, любой новый тип транспортного средства (например, Автомобиль, Грузовик, ..et * 1018) *) он должен реализовать IVehicle. Интерфейс IRental Я поместил только одно свойство, так как я не знаю, что вы хотите реализовать внутри реального класса, также как и другие классы. но поскольку вы реализуете конкретный класс, вы сможете определить основные свойства и метод, которые должны быть в интерфейсе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...