Основной вопрос по рефакторингу в абстрактный класс - PullRequest
6 голосов
/ 23 апреля 2009

Это может быть вопрос новичка, но существует ли стандартный способ рефакторинга дублирования свойства Wheel в абстрактный класс, но при этом сохраняется явное приведение к типу Part. Давайте предположим, что мы должны предотвратить установку FastCarWheel на SlowCar, и что есть много свойств, подобных этому.

abstract class Car {}

class FastCar : Car
{
    public FastCarWheel Wheel { get; set; }
} 

class SlowCar : Car
{
    public SlowCarWheel Wheel { get; set; }
} 

abstract class WheelPart {}

class FastCarWheel: WheelPart {}

class SlowCarWheel: WheelPart {}

В этом типе сценариев обычно разрешают этот тип дублирования? Я думал об использовании Generics, но мне кажется, что я перемещаю проблему, и она становится хуже для каждого дополнительного свойства, которое ведет себя таким образом.

abstract class Car <P>
    where P : Part
{
    protected abstract P Wheel { get; set; }
}

Спасибо

Ответы [ 5 ]

1 голос
/ 23 апреля 2009

Поскольку ваша цель состоит в том, чтобы позволить клиентскому коду вернуть свойство обратно как WheelPart, но установить его только как определенный подкласс, у вас есть несколько вариантов. Хотя боюсь, что ни один из них не очень чистый.

Во-первых, вы можете выдать ошибку времени выполнения, если задан неправильный тип:

    public abstract class Car
    {
        public abstract WheelPart Wheel { get; set; }
    }

    public class FastCar : Car
    {
        private FastWheel _wheel;
        public override WheelPart Wheel
        {
            get { return _wheel; }
            set
            {
                if (!(value is FastWheel))
                {
                    throw new ArgumentException("Supplied wheel must be Fast");
                }
                _wheel = (FastWheel)value;
            }
        }
    }

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

В противном случае вы могли бы разделить Getter и Setter для свойства, чтобы требуемый тип был очень ясным:

    public abstract class Car
    {
        public abstract WheelPart Wheel { get; }
    }

    public class FastCar : Car
    {
        private FastWheel _wheel;
        public override WheelPart Wheel
        {
            get { return _wheel; }
        }

        public void SetWheel(FastWheel wheel)
        {
            _wheel = wheel;
        }
    }

Это гораздо понятнее для клиента, и ИМХО, более удачное решение, если вам абсолютно необходимо представить геттер в качестве базового класса WheelPart.

1 голос
/ 23 апреля 2009

Я бы создал ICar и затем определил бы ваши Автомобили таким образом, вместо абстрактного класса

interface ICar
{
   IWheel Wheel {get; set;}
}

class FastCar: ICar
{
   FastWheel fastWheel;
   IWheel Wheel
   {
      get { return fastWheel; }
      set
      {
          if (value is FastWheel) fastWheel = (FastWheel)value;
      }    
   }         
}

class SlowCar: ICar
{
   SlowWheel slowWheel;
   IWheel Wheel
   {
      get { return slowWheel; }
      set
      {
          if (value is SlowWheel ) slowWheel = (SlowWheel )value;
      }    
   } 
}

class FastWheel: IWheel {}
class SlowWheel: IWheel {}
1 голос
/ 23 апреля 2009

Я думаю, использование политики Fast или Slow может помочь установить правильное колесо для данного типа автомобиля (где Car и Wheel зависят от политики и Car объект имеет, скажем, частную агрегацию колес).

1 голос
/ 23 апреля 2009

Это решение не полиморфно, но может быть единственным вариантом, если вам нужна видимость на уровне базового класса:

abstract class Car
{
    private CarWheel wheel;
    public CarWheel Wheel
    {
        get { return wheel; }
        protected set { wheel = value; }
    }
}

class FastCar : Car
{
    public new FastCarWheel Wheel
    {
        get { return base.Wheel as FastCarWheel; }
        set { base.Wheel = value; }
    }
}

class SlowCar : Car
{
    public new SlowCarWheel Wheel
    {
        get { return base.Wheel as SlowCarWheel ; }
        set { base.Wheel = value; }
    }
}

Возможно, вы захотите оценить, делает ли ваш базовый класс слишком много. Возможно, можно решить вашу проблему, разделив ваши классы на множество более мелких классов. С другой стороны, иногда это неизбежно.

0 голосов
/ 23 апреля 2009

Определить интерфейс колеса (IWheel):

public interface IWheel
{
}

Реализация интерфейса для FastCarWheel и SlowCarWheel, например,

public class FastCarWheel : IWheel
{
}

Теперь ваш абстрактный класс становится:

abstract class Car 
{
 public IWheel Wheel { get; set; }
}

Подклассы Car могут свободно использовать любую реализацию Wheel, которую они выбирают:

FastCar fastCar = new FastCar();
fastCar.Wheel = new FastCarWheel();
...