Наследование / Архитектура - PullRequest
0 голосов
/ 13 октября 2009

Ищите несколько советов о том, как структурировать / спроектировать приложение, и у меня возникли некоторые трудности. Пожалуйста, не стесняйтесь НЕ ограничивать ответы на технические детали, так как я не уверен, что делаю вещи наиболее эффективным способом. Я даю примеры в VB.NET, но, пожалуйста, не стесняйтесь использовать c #, так как это наш официальный целевой язык. Я просто знаю VB лучше. Также мы используем Visual Studio 2008.

Итак, сценарий: Мы пишем веб-приложение, которое (надеюсь) будет настраиваться для нескольких клиентов. Для краткости и чтобы не отдавать ферму, скажем, это приложение для персонала.

Итак, у меня будет базовый класс Person. Это будет только очень общие методы. Имеет государственную собственность Имя в классе Class1.

Просто ради аргументов я создам НОВЫЙ проект / сборку Person.Employee

Теперь, так как я мог бы хотеть продать это приложение модельным агентствам или строительным компаниям, возможно, я хочу добавить некоторые очень странные свойства. CheekStructure или CanOperateHeavyMachinery

Итак, я буду создавать новые проекты в студии под названием Person.Employee.TinasModeling & Person.Employee.TomsConstructionCompany который будет содержать код только для конкретной компании.

Вы можете предполагать, что каждая из этих сборок будет содержать ТОЛЬКО бизнес-логику, соответствующую уровню сборки в иерархии.

Помните, это приложение CUSTOM предназначено для многих компаний, но все они имеют дело с персоналом. Вот почему я придумала эту модель. Если мы обнаружим ошибку на более низком уровне, я просто хочу иметь возможность ее исправить и отправить dll или 2. Эта структура очень сильно нацелена на нас, возможно, имеющих 20 или более пользовательских приложений. Распределение и обслуживание - вот что касается PARAMOUNT. Существует не так много разработчиков. Больше, чем у меня, но меньше, чем на 3 грин.

Теперь проблема.

Для простоты, у меня есть Class1 лично.

В моем веб-приложении есть ссылка на Person.Employee.TinasModeling. Этот проект веб-приложения предназначен только для Tinas Modeling.

Если у меня есть
Imports Person.Employee.TinasModeling
...

Dim это как новый Class1
this.Name = "Некоторая ценность"

Это не скомпилируется, если у меня нет также ссылки на Person.

Вопрос 1. Действительно ли мне нужен ref для базового класса Person (и, вероятно, для Person.Employee), или есть какой-то способ обойтись без него? Я не хочу, чтобы кто-то случайно создавал классы на более низких уровнях цепочки наследования. Я не уверен, что хочу, чтобы они вообще создавали их на более низких уровнях, но, может быть, моя проблема спорная?

Вопрос 2. Является ли это лучшим способом построения этого? Пожалуйста, помните, что Maint и Distribution являются основными проблемами здесь. Представьте, что вы нашли ошибку на низком уровне и вам нужно открыть 20 решений, собрать каждое из них, протестировать (даже если оно автоматизировано), собрать и распространить. Я действительно хочу возможность отправить 1 Dll (или, может быть, ту, которая была обновлена, и зависимые тоже?) И будет выполнена, если dll более низкого уровня пройдет свои тесты

Если мне нужно иметь ссылки на все более низкие уровни дерева наследования, возможно, мне следует просто поместить все это в одну сборку под разными пространствами имен?

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

Спасибо заранее, Боб

Ответы [ 2 ]

1 голос
/ 13 октября 2009

Звучит как хороший кандидат для использования Dependency Injection. Для начала посмотрите этот пример на сайте Spring.NET. Извиняюсь за краткий ответ, но я могу уточнить, когда у меня будет больше времени, чтобы сесть и написать короткий пример, более подходящий для вашего поста.

EDIT: Хорошо, давайте сначала представим два дочерних объекта, TinasModeling.Model и BobTheBuilder.Builder

public class Model : IPerson
{
    private bool canAct;
    public bool CanAct
    {
        get { return canAct; }
        set { canAct = value; }
    }

    private double weight;
    public double Weight
    {
        get { return weight; }
        set { weight = value; }
    }

    public Model()
    {
    }

    public bool PayPerson(double Amount)
    {
        if (canAct)
            RequestFilmBoxOfficeRevenue();
        Pay();
    }

    public bool NewJobRequest()
    {
        SendPhotoPortfolioToClient();
        SendJobLocationToModel();
        if (canAct)
            NotifyFilmCrew();
        if (weight < 40.00)
            ContactNutritionist();
    }
}

public class Builder : IPerson
{
    private bool canOperateDigger;
    public bool CanOperateDigger
    {
        get { return canOperateDigger; }
        set { canOperateDigger = value; }
    }

    private double certifiedBuilder;
    public double CertifiedBuilder
    {
        get { return certifiedBuilder; }
        set { certifiedBuilder = value; }
    }

    public Builder()
    {
    }

    public bool PayPerson(double Amount)
    {
        ReuqestTimeSheet();
        Pay();
    }

    public bool NewJobRequest()
    {
        SendBluePrintsToBuilder();
        if (!certifiedBuilder)
            RequestLegalAdvice();
        if (canOperateDigger)
            RequestDrivingPermit();
    }

Оба класса реализованы в отдельных проектах - они не обязательны, но по причинам, которые я объясню позже. Как вы можете видеть, они оба работают по-разному, но имеют одну общую черту: они реализуют интерфейс IPerson.

public interface IPerson
{
    bool PayPerson(double Amount);
    bool NewJobRequest();
}

Этот интерфейс реализован, опять же, в отдельной сборке, чем оба вышеупомянутых объекта, и, следовательно, наши дочерние объекты ссылаются на эту сборку, давайте назовем ее BOL (Уровень бизнес-объектов). В BOL мы реализуем другой класс под названием PersonController

public class PersonController
{
    private IPerson thePerson;
    public IPerson ThePerson
    {
        get { return this.thePerson; }
        set { this.thePerson = value; }
    }

    public PersonController()
    {
    }

    public bool PayPerson(double Amount)
    {
        return this.thePerson.PayPerson(Amount);
    }

    public bool NewJob()
    {
        return this.thePerson.NewJobRequest();
    }
}

Самая важная вещь, которую следует отметить в PersonController, - это свойство ThePerson. Это будет наша точка внедрения зависимости. На этом этапе стоит отметить, что слой BOL полностью отделен от любых дочерних объектов. Это важная функция, обеспечивающая большую гибкость при развертывании и обслуживании.

Теперь мы используем и настраиваем то, что построили до сих пор. Наше приложение ссылается только на слой BOL (также отделяя наше основное приложение от любых дочерних объектов). В нашем основном приложении мы используем сборку Spring.NET Spring.Core, а в файл app.config добавляем следующее (для удобства чтения я опустил тег configSections)

  <spring>
<context>
  <resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
  <description>An  example that demonstrates simple IoC features.</description>
  <object name="Person"  type="BOL.BOL.PersonController, BOL.BOL">
    <property name="ThePerson" ref="Person"/>
  </object>
  <object name="Person" type="BobTheBuilder.BobTheBuilder.Builder, BobTheBuilder.BobTheBuilder">
  </object>
</objects>

Хорошо, надеюсь, все до сих пор со мной - первый элемент объекта относится к классу, который требует внедрения зависимости, в нашем случае PersonController. Дочерний элемент свойства ссылается на точку внедрения зависимости в PersonController или, в нашем случае, на свойство ThePerson. Атрибут ref этого элемента относится к нашему второму элементу объекта.

Второй элемент объекта, который мы устанавливаем, какой дочерний объект мы хотим внедрить во время выполнения (т.е. TinasModeling.Model или BobTheBuilder.Builder).

Наконец-то код, который собирает все вместе

    static void Main(string[] args)
    {
        IApplicationContext ctx = ContextRegistry.GetContext();
        PersonController person = (PersonController)ctx.GetObject("Person");

        // Ready to use person with implementation specified in config
        person.NewJob();
        person.PayPerson();
    }

Таким образом, это создаст экземпляр PersonController на основе того, как мы настроили «Person» в нашем app.config.

Таким образом, ответ на первый вопрос - нет (по крайней мере, как описано выше) - вы полностью отделяете свои базовые реализации.

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

Надеюсь, я достаточно хорошо объяснил себя.

0 голосов
/ 13 октября 2009

Как правило, все классы в корне дерева должны быть абстрактными, потому что все они неполные.

Когда только типы, специфичные для отдельного приложения клиента, становятся конкретными, ваша проблема непреднамеренного создания других классов исчезает.

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