Подтвердить что ты не робот

Создание сквозного кода, когда "предпочтительнее композиции по наследованию"

Проблема

Скажем, я пытаюсь моделировать мобильный телефон как комбинацию обычного телефона и КПК. Это своего рода сценарий множественного наследования (сотовый телефон - это телефон, и это КПК). Поскольку С# не поддерживает множественное наследование, это в значительной степени требует некоторой композиции. Кроме того, позвольте сказать, что у меня есть другие причины, чтобы поддержать композицию в любом случае.

Я всегда задаюсь вопросом: есть ли какие-либо инструменты, которые автоматически генерируют весь неизбежный сквозной код?

Позвольте мне изложить мой пример с каким-то фактическим кодом:

Интерфейсы:

public interface IPhone
{
    public void MakeCall(int phoneNumber);
    public void AnswerCall();
    public void HangUp();
}

public interface IPda
{
    public void SendEmail(string[] recipientList, string subject, string message);
    public int LookUpContactPhoneNumber(string contactName);
    public void SyncWithComputer();
}

Реализации:

public class Phone : IPhone
{
    public void MakeCall(int phoneNumber) { // implementation }
    public void AnswerCall() { // implementation }
    public void HangUp() { // implementation }
}

public class Pda : IPda
{
    public void SendEmail(string[] recipientList, string subject, string message) { // implementation }
    public int LookUpContactPhoneNumber(string contactName) { // implementation }
    public void SyncWithComputer() { // implementation }
}

Класс CellPhone

public class CellPhone : IPhone, IPda
{
    private IPhone _phone;
    private IPda _pda;

    public CellPhone(IPhone phone, IPda pda)
    {
        _phone = phone;
        _pda = pda;
    }

    public void MakeCall(int phoneNumber)
    {
        _phone.MakeCall(phoneNumber);
    }

    public void AnswerCall()
    {
        _phone.AnswerCall();
    }

    public void HangUp()
    {
        _phone.HangUp();
    }

    public void SendEmail(string[] recipientList, string subject, string message)
    {
        _pda.SendEmail(recipientList, subject, message);
    }

    public int LookUpContactPhoneNumber(string contactName)
    {
        return _pda.LookUpContactPhoneNumber(contactName);
    }

    public void SyncWithComputer()
    {
        _pda.SyncWithComputer();
    }
}

Написание класса CellPhone является утомительным и подверженным ошибкам:

Весь этот класс действительно действует как канал для классов Phone и Pda. На самом деле нет никаких оснований для того, чтобы человеческие усилия требовались для ввода всех этих сквозных утверждений (например, _phone.MakeCall(phoneNumber);). Он просто раскрывает открытый интерфейс полей нескольких членов.

Вопросы

  • Есть ли инструмент (желательно свободный:)), который спасет меня от ошибок, связанных с ошибкой написания сквозных методов? Я знаю, что я могу автоматически генерировать заглушки с помощью VS, но это только помогает мне на полпути.

  • Можете ли вы оценить целесообразность и желательность такой функции? Стоит ли предлагать Microsoft добавить такой тип генерации кода? Не могли бы вы его перенести, если бы я это сделал? Если нет, какие у вас есть возражения?

Edit

Кажется, что все говорят одно и то же: почему бы мне просто не сделать _phone и _pda в общедоступные свойства? Мое возражение против этого заключается в том, что оно нарушает "принцип наименьшего знания". Клиент моего класса CellPhone должен просто делать то, что делает сотовый телефон, ему не нужно иметь дело с выяснением, какие функции являются Phone функциями и которые являются Pda функциями. Это создает дополнительные зависимости и делает возможности интерфейса CellPhone менее очевидными.

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

4b9b3361

Ответ 1

Да, вы можете сгенерировать методы, используя блестящую надстройку VS, ReSharper

Выберите параметр generate delegating methods" из меню генерации кода (Alt-Insert с помощью схемы ярлыков по умолчанию).

Если у вас есть класс, который реализует интерфейс, содержащий поле, которое реализует один и тот же интерфейс, R # даст вам возможность генерировать код сквозной передачи. Он также может работать с любым количеством интерфейсов.

Ответ 2

Я видел это предложение раньше (не обязательно для). Это было давно. Я считаю, что автор создал проблему со связью, хотя я не знаю ее статуса.

Предлагаемый синтаксис был одним из следующих:

public class CellPhone : IPhone, IPda
{
    private readonly IPhone _phone implements IPhone;
    private readonly IPda _pda : IPda;

    // ...
}

Ответ 3

Добавление некоторых визуальных эффектов и деталей к ранее предоставленному ответу.

  • добавить интерфейс к классу, который вы хотите быть классом упаковки

    class MyWebElement : IWebElement { }
    

  1. Найти/нажать "Делегировать реализацию" YourInterfaceHere "в новое поле Реализация делегата
    1. Выберите свои параметры Параметры делегата
      1. Нажмите "Готово" и наслаждайтесь новым классом

        class MyWebElement : IWebElement
        {
            private IWebElement _webElementImplementation;
            public IWebElement FindElement(By @by)
            {
                return _webElementImplementation.FindElement(@by);
            }
        
            public ReadOnlyCollection<IWebElement> FindElements(By @by)
            {
                return _webElementImplementation.FindElements(@by);
            }
        
            public void Clear()
            {
                _webElementImplementation.Clear();
            }
        
            public void SendKeys(string text)
            {
                _webElementImplementation.SendKeys(text);
            }
        
            public void Submit()
            {
                _webElementImplementation.Submit();
            }
        
            public void Click()
            {
                _webElementImplementation.Click();
            }
        
            public string GetAttribute(string attributeName)
            {
                return _webElementImplementation.GetAttribute(attributeName);
            }
        
            public string GetCssValue(string propertyName)
            {
                return _webElementImplementation.GetCssValue(propertyName);
            }
        
            public string TagName
            {
                get { return _webElementImplementation.TagName; }
            }
        
            public string Text
            {
                get { return _webElementImplementation.Text; }
            }
        
            public bool Enabled
            {
                get { return _webElementImplementation.Enabled; }
            }
        
            public bool Selected
            {
                get { return _webElementImplementation.Selected; }
            }
        
            public Point Location
            {
                get { return _webElementImplementation.Location; }
            }
        
            public Size Size
            {
                get { return _webElementImplementation.Size; }
            }
        
            public bool Displayed
            {
                get { return _webElementImplementation.Displayed; }
            }
        }
        

Ответ 4

Вы можете проверить эту статью, которая входит в версию псевдо-множественного наследования в С#.

Ответ 5

Интересный вопрос. Может ли класс CellPhone действительно полагаться на реализацию методов IPhone, предоставляемых существующим, конкретным классом IPhone? Или более вероятно, что CellPhone понадобится какой-то пользовательский код, поддерживающий эти методы?

Глядя на ваш код (хотя я мало знаю о ваших требованиях) заставляет меня писать что-то вроде этого:

public class CellPhone 
{
    private Phone _phone;
    private Pda _pda;

    public CellPhone(Phone phone, Pda pda)
    {
        _phone = phone;
        _pda = pda;
    }

    public IPhone getPhone()
    {
        return _phone;
    }

    public IPda getPda()
    {
        return _pda;
    }

    public void MakeCall(string contactName) {
        int phoneNumber = _pda.LookUpContactPhoneNumber(contactName);
        _phone.MakeCall(phoneNumber);
    }

}

Вопрос 2:

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