Проблема наследования в C # - PullRequest
5 голосов
/ 18 августа 2011

Я рефакторинг некоторого кода и хочу, чтобы классы немного выше в цепочке наследования были немного более строгими с их параметрами.Поскольку я не уверен, что объясняю это правильно, вот что я получил:

public interface ISvdPredictor
{
    List<string> Users { get; set; }
    List<string> Artists { get; set; }
    float PredictRating(ISvdModel model, string user, string artist);
    float PredictRating(ISvdModel model, int userIndex, int artistIndex);
}

ISvdPredictor использует ISvdModel:

public interface ISvdModel
{
    float[,] UserFeatures { get; set; }
    float[,] ArtistFeatures { get; set; }
}

Теперь я хочуреализовать другой вариант:

public interface IBiasSvdPredictor : ISvdPredictor
{
    float PredictRating(IBiasSvdModel model, string user, string artist);
    float PredictRating(IBiasSvdModel model, int userIndex, int artistIndex);
}

, использующий IBiasSvdModel, производный от ISvdModel:

public interface IBiasSvdModel : ISvdModel
{
    float GlobalAverage { get; set; }
    float[] UserBias { get; set; }
    float[] ArtistBias { get; set; }
}

IBiasSvdPredictor не будет работать с ISvdModel.

Проблема в том, что когда я реализую IBiasSvdPredictor, мне придется реализовать 2 пары методов PredictRating.Один из ISvdPredictor, а другой из IBiasSvdPredictor.Что мне нужно сделать, чтобы иметь возможность просто реализовать те из IBiasSvdPredictor?

Я также попробовал дженерики, но не смог ограничить PredictRating для BiasSvdPredictor до IBiasSvdModel, используядиректива where.Возможно, я все делаю неправильно, поэтому любое предложение может помочь.Я думаю, вы понимаете, что я пытаюсь сделать.

РЕДАКТИРОВАТЬ: Если кому-то нужно больше контекста, см. https://github.com/gligoran/RecommendationSystem. Я пишу этот код для моей диссертации для бакалавра.

Ответы [ 6 ]

4 голосов
/ 18 августа 2011

Вы можете использовать дженерики и ограничения.

public interface ISvdModel
{
    float[,] UserFeatures { get; set; }
    float[,] ArtistFeatures { get; set; }
}

public interface IBiasSvdModel : ISvdModel
{
    float GlobalAverage { get; set; }
    float[] UserBias { get; set; }
    float[] ArtistBias { get; set; }
}

public interface ISvdPredictor<in TSvdModel>
    where TSvdModel : ISvdModel // Require that TSvdModel implements ISvdModel
{
    List<string> Users { get; set; }
    List<string> Artists { get; set; }

    float PredictRating(TSvdModel model, string user, string artist);
    float PredictRating(TSvdModel model, int userIndex, int artistIndex);
}

// I would actually avoid declaring this interface. Rather, see comment on the class.
public interface IBiasSvdPredictor : ISvdPredictor<IBiasSvdModel> { }

class BiasSvdPredictor : IBiasSvdPredictor // Preferred : ISvdPredictor<IBiasSvdModel>
{
    // ...
    public float PredictRating(IBiasSvdModel model, string user, string artist) { }
    public float PredictRating(IBiasSvdModel model, int userIndex, int artistIndex) { }
}
2 голосов
/ 18 августа 2011

У interface должен быть один метод, PredictRating. У меня не было бы двух интерфейсов, которые могли бы реализовать один и тот же метод. Смешение.

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

Один интерфейс, Один абстрактный класс. N конкретный класс, который реализует PredictRating по своему усмотрению.

 public interface Demo
    {
        int PredictRating(int param1);
    }

    public abstract class AbstractDemo : Demo
    {
        public virtual int PredictRating(int param1)
        {
            return param1 + 1;
        }
    }

    public class ClassDemo1 : AbstractDemo
    {
        //This guy uses AbstractDemo Predict Rating
        public override int PredictRating(int param1)
        {
            return base.PredictRating(param1);
        }
    }

    public class ClassDemo2 : AbstractDemo
    {
        //This guy overrides the predict rating behavior
        public override int PredictRating(int param1)
        {
            return param1 + 2;
        }
    }
1 голос
/ 18 августа 2011

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

public class Foo : IBiasSvdPredictor {
    public float PredictRating(IBiasSvdModel, string user, string artist) { .... }

    // this is an expicit implementation of ISvdPredictor's method. You satisfy
    // the interface, but this method is not a public part of the class. You have to
    // cast the object to ISvdPredictor in order to use this method.
    float ISvdPredictor.PredictRating(ISvdModel model, string user, string artist) {
        this.PredictRating((IBiasSvdModel)model, user, artist);
    }
}

Это, конечно, не будет работать, если ISvdModel на самом деле не IBiasSvdModel.

0 голосов
/ 18 августа 2011

Без полного понимания вашей объектной модели (так что это может на самом деле не применяться в вашей ситуации), кажется, что ISvdModel не должно быть частью определения интерфейса.Это больше похоже на детали реализации, а не на ту часть контракта, которую вы пытаетесь применить.Для меня имеет больше смысла передавать ISvdModel (или IBiasSvdModel) в конструктор вашего класса реализации, а не иметь его как часть вашего ISvdPredictor интерфейса.Тогда вам вообще не понадобятся 2 отдельных определения интерфейса, у вас просто будет 2 реализации одного интерфейса.

Возможно, вы даже сможете сделать еще один шаг вперед;если единственное различие между ISvdPredictor и IBiasSvdPredictor состоит в том, что один использует ISvdModel, а другой использует IBiasSvdModel, вам даже не понадобятся 2 реализации, только одна, и вы передадите правильный экземплярISvdModel для каждой ситуации.Это шаблон проектирования, называемый Inversion of Control, специально использующий Dependency Injection, и он очень эффективен для достижения более высоких уровней повторного использования кода в ваших программах.

0 голосов
/ 18 августа 2011

Мне нужно реализовать 2 пары методов PredictRating.

Конечно, вы делаете.Что вы ожидали?

Если ваш IBiasSvdPredictor должен взять IBiasSvdModel в своем методе PredictRating, то IBiasSvdPredictor будет , а не и ISvdPredictor (поскольку он не может принимать ISvdModel в качестве первого параметра для PredictRating), а наследование IBiasSvdPredictor от ISvdPredictor - неправильный выбор.

По моему мнению, вы должны просто хранить интерфейсы отдельнои не наследовать одно от другого.

0 голосов
/ 18 августа 2011

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

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