Дизайн класса C # без использования «Internal» или «Static»? - PullRequest
1 голос
/ 21 сентября 2009

У меня есть куча данных, которые я хочу создать в классе, и для каждой переменной я хочу убедиться, что определенный набор методов также определен. IE:

[TypeA] VarA
[TypeB] VarB
[TypeC] VarC

FA1() which is a function of VarA and VarB
FA2() which is a function of VarA and VarC

FB1() which is a function of VarB and VarA
FB2() which is a function of VarB and VarC
...

Поскольку будет большое количество переменных (и, следовательно, еще больше функций), я хочу разделить мой исходный код на управляемые куски. Поэтому я ищу автоматический способ обеспечения создания всех функций для каждой переменной.

Я предложил 3 возможных метода организации своего кода, и я не слишком доволен каждым из них, и я ищу или советую, какой метод лучше (или даже если я пропустил совершенно другой метод реализации) ):

1. Частичный класс

partial class Base
{
}

partial class Base 
{
  [TypeA] VarA;

  FA1 { .. };  // function of VarA and VarB
  FA2 { .. };  // function of VarA and VarC
}


partial class Base 
{
  [TypeB] VarB;

  FB1 { .. };  // function of VarB and VarA
  FB2 { .. };  // function of VarB and VarC
}

Плюсы:

  1. Простой
  2. Переменные доступны только из класса Base.
  3. Если есть две переменные одного типа, то функции для каждой переменной могут реализовывать свою собственную функцию по-разному.

Минусы:

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

Обратите внимание, что минусы могут быть решены с помощью генератора кода какого-то рода (может быть, время, чтобы изучить T4 ??)


2. Внутренний класс

class Base 
{
  internal [ClassA] ObjA = new [ClassA]();
  internal [ClassB] ObjB = new [ClassB]();
}

class [BaseClassA]
{
  public [TypeA] VarA;

  public virtual F1 { .. };
  public virtual F2 { .. };
}

class [ClassA] : [BassClassA]
{
  public override F1 { .. };  // function of VarA and ObjB.VarB
  public override F2 { .. };  // function of VarA and ObjC.VarC
}
...

Плюсы:

  1. Иерархия классов обеспечивает создание всех функций и доступ к переменным.
  2. Посредством использования виртуальных функций можно создавать экземпляры, специфичные для реализации функций

Минусы:

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

3. Статические данные

abstract class Data
{
   static [TypeA] VarA;
   static [TypeB] VarB;
   ...
}

abstract class [BaseClassA] : Data
{
  public virtual F1 { .. };
  public virtual F2 { .. };
}

class [ClassA] : [BassClassA]
{
  public override F1 { .. };  // function of VarA and VarB
  public override F2 { .. };  // function of VarA and VarC
}

class Base 
{
 [ClassA] ObjA = new [ClassA]();
 [ClassB] ObjB = new [ClassB]();
}

Плюсы:

  1. Система гарантирует, что все подпрограммы будут созданы
  2. Данные не обрабатываются по всей сборке
  3. Внутри каждой функции вы можете напрямую ссылаться на другие переменные в соответствии с решением «частичного класса»

Минусы:

  1. Использование static пахнет так, будто я только что заново изобрел глобальные данные.

Что я хочу, так это как-то черри выбрать лучшие пункты каждого метода:

  1. Прямой способ доступа к переменным методов "Partial class" и "Static"
  2. Локальные данные метода "Частичный класс"
  3. Автоматическое принудительное выполнение функции "Внутренний" и "Статический" методы.

И я хочу избежать:

  1. Отсутствие принудительной генерации функций в "Частичном классе"
  2. Глобальный доступ к данным в «внутреннем» методе
  3. Переосмысление глобальных данных в «статическом» методе

Если бы у меня были мои барабанщики, я бы сказал, что я хочу как-то применить интерфейс к экземпляру переменной - например:

[TypeA] VarA : IFunctions;
[TypeB] VarB : IFunctions;

И каким-то образом компилятор автоматически сгенерирует окончательные имена функций из имен интерфейсов и допустимого имени.

Таким образом, люди могут предложить, какой из 3 методов они предпочтут реализовать, или предложить любые другие методы, которые могут подойти.

Ответы [ 3 ]

2 голосов
/ 22 сентября 2009

Вы представляете четыре примера кода, «простую» версию, чтобы вы могли объяснить проблему, а затем 3 «лучших» решения, чтобы решить проблему. Единственная версия, которая была очевидна, была простая версия. Итак, я думаю о плохом разработчике, который должен прийти на поддержку в следующем году (это может быть вы, забыв, что вы сделали).

Итак, не могли бы вы рассмотреть другой механизм для "обеспечения того, чтобы были созданы все функции для каждой переменной". Вы упомянули о готовности использовать T4 для автоматической генерации заглушек для вас во время компиляции. Как насчет использования Microsoft FxCop для отлова любых случаев, когда вы забыли что-то добавить.

Если вы с ним не знакомы, Microsoft FxCop (также встроенный в некоторые разновидности Visual Studio) сканирует скомпилированные сборки и оценивает сотни правил каркаса в соответствии с вашим кодом на основе правильного написания и обработки переменные для неиспользованных местных жителей.

Хотя я лично согласен с большинством правил, которые Microsoft включила в FxCop, я думаю, что настоящая красота этого заключается в способности создавать свои собственные правила. Я создал правила, которые добавляю в FxCop, которые обеспечивают соблюдение принципов CRUD, например, если у вас есть метод CreateXxx, у вас должен быть метод DeleteXxx и т. Д. Итак, если вы идентифицируете класс, который соответствует желаемому шаблону, вы может получить список всех переменных {A, B, C}, а затем гарантировать, что FuncAB (A, B) существует и FuncAC (A, C) существует и т. д.

Тогда FxCop поймает даже младшего разработчика в следующий раз, когда он реализует IBiVariableFunction и забудет функцию в паре.

Приветствия, Адриан

0 голосов
/ 21 сентября 2009

Не могли бы вы использовать предложение 2, но с защищенным вместо внутреннего?

0 голосов
/ 21 сентября 2009

Ваш вопрос в значительной степени не имеет реального контекста и его трудно понять. Вы предоставили три «ответа» без четкого вопроса (imo.)

Честно говоря, если вы хотите убедиться, что каждая «переменная», которую вы называете, имеет связанные методы, вам следует рассмотреть возможность использования интерфейсов и использования свойств вместо полей (поскольку интерфейсы не могут указывать поля.)

interface IAStuff {
    TypeA AProp { get; }
    void DoSomethingToA();
}

interface IBStuff {
    TypeB BProp { get; }
    void DoSomethingToB();
}

public class Foo : IAStuff, IBStuff {
    TypeA AProp { get; private set; }
    TypeB BProp { get; private set; }

    void DoSomethingToA() { ... }
    void DoSomethingToB() { ... }
}

Если класс объявляет, что он реализует интерфейс, у него нет другого выбора, кроме как предоставить указанные члены, или он не будет компилироваться.

Надеюсь, это поможет,

-Oisin

...