Я работаю над проектом, который имеет множество классов, которые происходят из класса View, где View предоставляет некоторые распространенные методы и где производные классы имеют поля, которые ссылаются на элементы пользовательского интерфейса, специфичные для этого представления. Например (в C#):Надежный способ генерации полей производного класса в базовом классе?
public abstract class View
{
public virtual void Initialize(){}
public virtual void Activate(){}
public virtual void Deactivate(){}
}
public class MainScreenView : View
{
private ImageView portraitImageView;
private ImageView landscapeImageView;
public MainScreenView(ImageView portrait, ImageView landscape)
{
portraitImageView = portrait;
landscapeImageView = landscape;
}
public override Initialize()
{
base.Initialize();
portraitImageView.Initialize(); // I would like to eliminate these calls!
landscapeImageView.Initialize();
}
public ImageView GetPortrait() { return portraitImageView; }
public ImageView GetLandscape() { return landscapeImageView; }
}
public class ImageView : View
{
private Image image;
public ImageView(Image image) { this.image = image; }
public override void Initialize() { base.Initialize(); image.Show(); }
public Image GetImage() { return image; }
}
В этом примере я должен позвонить Initialize() на всех ImageViews когда MainScreenView.Initialize называется. Это кажется неприемлемым и неудобным для ошибок, потому что вызов Initialize() должен быть добавлен каждый раз при добавлении нового подзадача в композицию MainScreenView. Поэтому я хотел бы исключить необходимость этих вызовов в производных классах, но я хочу поддерживать поля в полях, специфичных для представления.
Моя идея заключается в том, чтобы добавить коллекцию представлений для базового класса, который затем может рекурсивно быть инициализирован() следующим образом:
public abstract class View
{
private List<View> subViews;
public virtual void Initialize()
{
foreach(View in subViews) { view.Initialize(); }
}
// This gets called before Initialize() is called.
public void AddSubViews(View[] views)
{
subViews = new List<View>();
subViews.AddRange(views);
}
}
public class MainScreenView : View
{
private ImageView portraitImageView;
private ImageView landscapeImageView;
public MainScreenView()
{
portraitImageView = ???;
landscapeImageView = ???;
}
// Even if View.subViews had been protected instead of private, this couldn't return an element from the list because the required index is unknown.
public ImageView GetPortrait() { return portraitImageView; }
public ImageView GetLandscape() { return landscapeImageView; }
}
public class ImageView : View
{
private Image image;
public ImageView() { this.image = ??? }
public override void Initialize() { base.Initialize(); image.Show(); }
public Image GetImage() { return image; } // Even if View.subViews had been protected instead of private, this couldn't return an element from the list because the required index is unknown.
}
Однако, поскольку все отдельный суб-взгляды сейчас «анонимные '(к ним обращаются по индексу вместо имени поля), это не сработает для меня, если я также не добавлю суб-представления через конструктор производного класса, как это было в моем первом примере, где я не могу обеспечить соблюдение что объекты, переданные в конструктор, являются теми же объектами, которые находятся в списке, или вызывают AddSubViews из конструктора производного класса, где под-представления добавляются вручную каждый раз, когда добавляется новый под-просмотр ... который имеет тот же выдать как вызов Initialize() на под-представлениях в производных классах.
Так что мой вопрос: есть способ иметь все вызовы инициализации под-представлений, выполняемые в базовом классе View, при этом все же быть в состоянии предоставить элементы, относящиеся к производному классу, без передачи ссылок на эти элементы на конструктор производного класса?
Предпочитаете использовать интерфейсы (например, 'IView') над базовыми абстрактными классами. Вы можете получить только один класс в C#, и нет необходимости создавать зависимость от базового абстрактного класса во всей вашей программе. Пока интерфейс 'IView' будет общедоступным, ваш базовый класс может быть оставлен« внутренним »и скрыт от других сборок. Кроме того, тот факт, что каждый * конкретный * вид будет иметь свои собственные * специальные * общедоступные методы, означает, что нет возможности фактически абстрагировать их использование, поэтому я не уверен, как вы планируете использовать эти методы вообще. – Groo