Генерация сквозного кода при «предпочтении композиции над наследованием» - PullRequest
19 голосов
/ 28 января 2010

Задача

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

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

Позвольте мне привести в пример мой реальный код:

Интерфейсы:

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 }
}

Класс сотового телефона

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);). Он просто открывает доступ к общедоступному интерфейсу пары полей-членов.

Вопросы

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

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

Редактировать

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

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

Ответы [ 5 ]

17 голосов
/ 28 января 2010

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

Выберите « генерировать методы делегирования » в меню генерации кода (Alt-Insert со схемой ярлыков по умолчанию).

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

2 голосов
/ 28 января 2010

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

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

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

    // ...
}
1 голос
/ 11 января 2017

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

  1. добавить интерфейс к классу, который вы хотите использовать в качестве класса упаковки

    class MyWebElement : IWebElement { }
    

  1. Найти / Нажать «Делегировать реализацию« YourInterfaceHere »в новое поле Delegate Implementation

  1. Выберите ваши варианты Delegate options

  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; }
        }
    }
    
0 голосов
/ 28 января 2010

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

0 голосов
/ 28 января 2010

Интересный вопрос. Может ли класс 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:

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

...