Шаблон проектирования для преодоления обратного порядка конструктора? - PullRequest
0 голосов
/ 21 февраля 2019

У меня следующая проблема:

Базовый класс ожидает получения некоторых данных, но данные инициализируются конструктором производного класса, который в C # вызывается после вызова базового конструктора.

Контекст / Что я пытаюсь решить:

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

Производные классы, например, Track1 каждый выбирает данные дорожки из определенного формата файла, со значительными различиями, которые запрещают реализацию всего кода в базовом классе Track.

Основная задача Track состоит в том, чтобы абстрагировать данные, поступающие из производных классов, и для этого в нем есть абстрактные члены, которые должны реализовывать производные классы, например, int GetVertexCount, Vector3 GetVertex(int).

.это интерфейс IPicture, который может загружаться из разных форматов, например, BMP, JPEG, и возвращать все это как абстракцию.

Проблема, с которой я сталкиваюсь:

ВC #, конструкторы базового класса вызываются до конструктора производного класса, но я должен инициализировать что-то в конструкторе класса производного , который, в свою очередь, я должен передать конструктору класса base .И пока я нахожусь на нем, я хотел бы, чтобы члены были неизменяемыми, то есть readonly.

Вопрос:

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

Ответ:

После @ Kit ответ вот как я закончили все нормально:

enter image description here

По иронии судьбы, он оказался C-подобным API:)

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

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

Вот упрощенный пример

public class Base
{
     protected Base(SomeType data)
     {
         // base logic using data
     }
}

public class DerivedOne : Base
{
    public DerivedOne(int some, string data) : base(DerivedLogic(some, data))
    {
        ...
    }

    private static SomeType DerivedLogic(int some, string data) => ...
}

public class DerivedTwo : Base
{
    public DerivedTwo (string moreStuff) : base(DerivedLogic(moreStuff))
    {
        ...
    }

    private static SomeType DerivedLogic(string moreStuff) => ...
}

Это выполняется в следующем порядке:

  1. Статический метод DerivedLogic
  2. Конструктор базового класса (с использованием значения из DerivedLogic)
  3. Производный конструктор

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

public class Base
{
     protected Base(SomeOtherType dataWrapper)
     {
         var data = dataWrapper.DerivedLogic();
         // base logic using data
     }
}

public class DerivedOne : Base
{
    public DerivedOne(SomeOtherType otherType) : base(otherType)
    {
        ...
    }
}

Или вычислите SomeType где-нибудь до вызова каких-либо конструкторов, а затем передайте его. Любой из этих способов является лучшим вариантом, поскольку он следует за SRP * 1023.*:

  1. Базовый класс, отвечающий за то, что он делает.
  2. Та же ответственность лежит на логике построения дорожки.
  3. Производный класс несет единственную ответственность.
0 голосов
/ 21 февраля 2019

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

Существует множество других подходов, которые вы можете использовать, например, использование статического Create() метода.

class Derived : Base
{
    private readonly object _o;

    private Derived(object o, string s) : base(s)
    {
        _o = o;
    }

    public static Derived Create(string path)
    {
        var o = new object();// initialize from path
        var s = o.ToString(); // get s from o.
        return new Derived(o, s)
    }
}

Вы также можете рассмотретьиспользуя композицию вместо наследования:

class Base
{
    private readonly string _s;

    public Base(string s)
    {
        _s = s.ToLower();
    }
}

class Derived
{
    private readonly object _o;
    private readonly Base _b;

    public Derived(string path)
    {
        _o = new object();// initialize from path
        _b = new Base(_o.ToString());
    }       
}

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

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