Если Вы надеетесь использовать мощные, высокоуровневые инструменты вместо того, чтобы создать все сами, проходя проекты и чтения для , этот курс является довольно хорошим вариантом. Это - курс языков автором механизма синтаксического анализатора Java ANTLR. Можно получить книгу для курса как PDF от Прагматически настроенные Программисты .
курс пробегается через стандартный материал компилятора компилятора, который Вы видели бы в другом месте: парсинг, типы и проверка типа, полиморфизм, таблицы символов и генерация кода. В значительной степени единственной вещью, которая не покрыта, является оптимизация. Заключительный проект является программой что компиляции подмножество C. Поскольку Вы используете инструменты как ANTLR и LLVM, выполнимо записать весь компилятор в единственный день (у меня есть доказательство существования этого, хотя я действительно имею в виду ~24 часа). Это тяжело на практической разработке с помощью современных инструментов, немного легче на теории.
LLVM, между прочим, является просто фантастическим. Много ситуаций, где Вы могли бы обычно компилировать вниз в блок, Вы будете очень более обеспеченной компиляцией в Промежуточное Представление LLVM вместо этого. Это является высокоуровневым, кросс-платформенным, и LLVM довольно хорош в генерации оптимизированного блока от него.
Это слишком много, чтобы поместить его в конструктор любого класса, не говоря уже о базовом классе. Я предлагаю вам выделить это в отдельный метод Initialize
.
Я бы подумал о создании абстрактной фабрики , которая отвечает за создание и инициализацию экземпляров ваших производных классов с использованием метода шаблона для инициализации.
] В качестве примера:
public abstract class Widget
{
protected abstract void InitializeStep1();
protected abstract void InitializeStep2();
protected abstract void InitializeStep3();
protected internal void Initialize()
{
InitializeStep1();
InitializeStep2();
InitializeStep3();
}
protected Widget() { }
}
public static class WidgetFactory
{
public static CreateWidget<T>() where T : Widget, new()
{
T newWidget = new T();
newWidget.Initialize();
return newWidget;
}
}
// consumer code...
var someWidget = WidgetFactory.CreateWidget<DerivedWidget>();
Этот заводской код можно значительно улучшить - особенно если вы хотите использовать контейнер IoC для выполнения этой обязанности ...
Если у вас нет контроля над производными классами, вы можете не иметь возможности помешать им предложить открытый конструктор, который можно вызвать - но, по крайней мере, вы можете установить шаблон использования, которого потребители могли бы придерживаться.
Не всегда возможно запретить пользователям ваших классов стрелять себе в ногу - но,вы можете предоставить инфраструктуру, которая поможет потребителям правильно использовать ваш код, когда они ознакомятся с дизайном.
Во многих случаях инициализация включает в себя присвоение некоторых свойств. Можно сделать сами эти свойства абстрактными
и иметь производный класс, переопределяющий их и возвращающий некоторое значение вместо передачи значения в базовый конструктор для установки. Конечно, применимость этой идеи зависит от характера вашего конкретного класса. В любом случае, иметь такой объем кода в конструкторе неприятно.
На первый взгляд, я бы предложил перенести такую логику в методы, основанные на этой инициализации. Что-то вроде
public class Base
{
private void Initialize()
{
// do whatever necessary to initialize
}
public void UseMe()
{
if (!_initialized) Initialize();
// do work
}
}
Поскольку шаг 1 «захватывает файл», было бы неплохо иметь Initialize (IBaseFile) и пропустить шаг 1. Таким образом, потребитель может получить файл, как ему заблагорассудится - поскольку это абстрактный в любом случае. Вы по-прежнему можете предложить «StepOneGetFile ()» в качестве абстрактного, возвращающего файл, чтобы они могли реализовать его таким образом, если захотят.
DerivedClass foo = DerivedClass();
foo.Initialize(StepOneGetFile('filepath'));
foo.DoWork();
Изменить: Я почему-то ответил на это за C ++. Извините. Для C # я рекомендую не использовать метод Create ()
- используйте конструктор и убедитесь, что объекты остаются в допустимом состоянии с самого начала. C # разрешает виртуальные вызовы из конструктора, и их можно использовать, если вы тщательно документируете их ожидаемую функцию, а также предварительные и последующие условия. Я сделал вывод о C ++ в первый раз, потому что он не разрешает виртуальные вызовы из конструктора.
Сделайте отдельные функции инициализации закрытыми
. Может быть как частным
, так и виртуальным
. Затем предложите общедоступную невиртуальную функцию Initialize ()
, которая вызывает их в правильном порядке.
Вы можете использовать следующий прием, чтобы убедиться, что инициализация выполняется в правильном порядке. Предположительно, у вас есть некоторые другие методы ( DoActualWork ), реализованные в базовом классе, которые зависят от инициализации.
abstract class Base { private bool _initialized; protected abstract void InitilaizationStep1(); private void InitilaizationStep2() { return; } protected abstract void InitilaizationStep3(); protected Initialize() { // it is safe to call virtual methods here InitilaizationStep1(); InitilaizationStep2(); InitilaizationStep3(); // mark the object as initialized correctly _initialized = true; } public void DoActualWork() { if (!_initialized) Initialize(); Console.WriteLine("We are certainly initialized now"); } }
Я бы не стал этого делать. Я обычно считаю, что выполнение любой «реальной» работы в конструкторе в конечном итоге будет плохой идеей.
Как минимум, иметь отдельный метод для загрузки данных из файла. Вы можете привести аргумент, чтобы пойти дальше и иметь отдельный объект, отвечающий за построение одного из ваших объектов из файла, разделив проблемы «загрузки с диска» и операций в памяти над объектом.