Представление бизнес-сущности в качестве компонента

BE-BC.png


ПроблематикаПравить

Если мы программируем некую цельную систему, мы достаточно быстро заметим, что сложные бизнес-сущности состоят из частей, которые ссылаются на более простые бизнес-сущности. Так например, в описании платежа мы встретим такие понятия как клиент, различные банки, пользователи этой системы. В платеже они будут представлены компонентом, состоящем из минимальной идентификации соответствующей сущности. Так, например, о банке нам будет интересно только наименование, адрес, swift, и еще немногое число его свойств. При этом в системе конечно же существует справочник в котором содержится более полная информация обо всех используемых банках. При этом в этом случае информация о банке будет визуально представлена т.н. карточкой, в которой будет уже порядка 30-50 различных свойств о банке.

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

По сути мы имеем две иерархии:

  1. бизнес-сущностей: лицо, физическое лицо, юридическое лицо и банк
  2. компонентов: банк, клиент и пользователь

При этом когда мы используем компоненты, мы частично используем бизнес-сущности. При этом это может быть не один в один, а по достаточно своеобразной логике. Наиболее просто связана бизнес-сущность (карточка) банка с компонентом банк - тут связь один в один. Но тем не менее уже тут описано, что банк это частный случай юридического лица, которое в свою очередь частный случай лиц. А компонент представляет собой лишь частный случай бизнес-сущности банк, интересуясь 5-10 его свойствами. Аналогично и с компонентом пользователя (например, дилер банка, осуществляющий конвертацию в платеже), который может быть ассоциирован только с физическим лицом. Когда же мы говорим о компоненте клиент, это достаточно общая сущность, это может быть и физическое лицо, и юридическое и банк. Поэтому тут мы можем иметь разные связи: банк-клиент, физическое лицо-клиент и в принципе любой наследник лица может быть представлен компонентом клиент.

РеализацияПравить

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

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

ПримерПравить

    /// Интерфейс "Клиент"
    public interface IClient : ICloneable
    {
        /// Идентификатор клиента
        string PIN { get; set; }
        /// Название клиента
        string Name { get; set; }
        /// Адрес клиента
        AddressStruct Address { get; set; }

        /// Регистрационный номер, если нету разрешается null
        string RegistrationNumber { get; set; }
        /// Код налогоплательцика, если нету разрешается null
        string TaxPayerCode { get; set; }

        /// код банка в указанной в нац. кодировке, если нету разрешается null
        string BankCode { get; set; }

        /// Изменение идентификатора клиента
        event EventHandler ChangePIN;
        /// Изменение названия клиента
        event EventHandler ChangeName;
        /// изменение кода банка в любой системе классификации банков
        event EventHandler ChangeBankCode;

    }


    /// Карточка персон
    public class Person : IClient
    {

        #region IClient Realization

        /// Изменение PIN
        public virtual event EventHandler ChangePIN;
        /// Изменение названия
        public virtual event EventHandler ChangeName;
        /// Изменение кода
        event EventHandler IClient.ChangeBankCode
        {
            add { }
            remove { }
        }


        /// Идентификатор персоны
        public virtual string PIN
        {
            get { return personID; }
            set
            {
                if (personID != value)
                {
                    personID = value;
                    if (ChangePIN != null)
                    { ChangePIN(null, new EventArgs()); }
                    OnPropertyChanged("PIN");
                }
            }
        }
        /// Название (описание) персоны
        public virtual string Name
        {
            get { return name; }
            set
            {
                if (name != value)
                {
                    name = value;
                    if (ChangeName != null)
                    { ChangeName(null, new EventArgs()); }
                    OnPropertyChanged("Name");
                }
            }
        }

        /// <summary>
        /// Адрес персоны
        /// </summary>
        public virtual AddressStruct Address
        {
            get { return address; }
            set { address = value; }
        }

        /// Регистрационный номер
        string IClient.RegistrationNumber
        {
            get { return null; }
            set { }
        }
        /// Код налогоплательщика
        string IClient.TaxPayerCode
        {
            get { return null; }
            set { }
        }
        /// код банка в указанной в нац. кодировке
        string IClient.BankCode
        {
            get { return null; }
            set { }
        }

        #endregion

        #region Fields
        /// код персоны.
        protected string personID = "";
        /// название
        protected string name = "";
        /// адрес
        protected AddressStruct address = new AddressStruct();

        /// дата регистрации клиента
        protected DateTime registrationDate;
        /// признак, определяющий резидентность
        /// 1 - резидент
        /// 2 - не резидент
        protected string resInd = "";
        /// статус по черному списку
        protected string blackListStatus = "";

        #endregion
    }

    /// Карточка организации
    public class Institution : Person, IClient
    {
        #region IClient Realization
        /// Регистрационный номер
        public string RegistrationNumber
        {
            get { return registrationNumber; }
            set { registrationNumber = value; }
        }
        /// Код налогоплательщика
        public string TaxPayerCode
        {
            get { return taxPayerCode; }
            set { taxPayerCode = value; }
        }
        #endregion

        #region Fields
        /// регистрационный номер
        protected string registrationNumber = "";
        /// код налогоплательщика
        protected string taxPayerCode = "";
        #endregion

    }

    /// Карточка банка
    public class Bank : Institution, IClient
    {
        #region IClient Realization

        /// изменение кода банка в любой системе классификации банков
        public event EventHandler ChangeBankCode;

        /// код банка
        public string BankCode
        {
            get { return bankCode; }
            set
            {
                if (!bankCode.Equals(value))
                {
                    bankCode = value;
                    if (ChangeBankCode != null)
                    { ChangeBankCode(null, new EventArgs()); }
                    OnPropertyChanged("BankCode");
                }
            }
        }

        #endregion

        #region Fields
        /// код банка
        private string bankCode="";
        /// категория банка:
        /// 0 - "наш" банк, 1 - нац. банк, 2 - обычный банк, 3 - другое кредитное учреждение
        private string bankMark = "";
        /// признак, определяющий наличие корротношений с банком
        /// 0 - нет, 1 - есть
        private string corrBank = "";
        #endregion
    }

    /// Физическое лицо
    public class Man : Person, IUser
    {
        #region IUser Realization
        /// специальность пользователя
        ///(D - дилер, P - кассир, O - операционист)
        public string ProfType
        {
            get { return profType; }
            set
            {
               profType = value;
            }
        }
        #endregion

        #region fields
        /// специальность пользователя
        ///(D - дилер, P - кассир, O - операционист)
        private string profType;
        #endregion
    }

    /// Управляющая часть компонента "Клиент"
    public class ClientController : EventComponentController
    {
        /// Визуализация компонента
        IPersonSelectLayout pClientLayoutMini;
        /// Визуализация компонента
        InstitutionLayout pClientLayout;
        /// Тело компонента
        ClientComponent body;

        protected override BusinessComponent Body
        { get { return body; } }

        /// Тело компонента
        public override object ControlledBody
        {
            get { return body.Client; }
            set { body.Client = (IClient)value; }
        }


        /// Текущая визуализация
        public override UserControlBase ActiveLayout
        {
            get
            {
                if (pClientLayoutMini != null)
                {
                    return pClientLayoutMini.Layout;
                }
                if (pClientLayout != null)
                {
                    return pClientLayout;
                }
                return null;
            }
        }

        /// Конструктор
        public ClientController(ClientComponent argClientComponent,
            IPersonSelectLayout argPersonSelectLayout)
        {
            body = argClientComponent;
            body.Controller = this;
            pClientLayoutMini = argPersonSelectLayout;
            pClientLayoutMini.Layout.DataContext = body.Client;
        }

        /// Конструктор
        public ClientController(ClientComponent argClientComponent,
            InstitutionLayout argInstitutionLayout)
        {
            body = argClientComponent;
            body.Controller = this;
            pClientLayout = argInstitutionLayout;
            pClientLayout.DataContext = body.Client;
        }

        /// Создание схемы событий для данных
        public override void CreateEventSchemeForBody()
        {
            if (body.Client != null)
            {
                body.Client.ChangePIN += new EventHandler(body.Find);
            }
        }

        /// Создание схемы событий для интерфейсного изображения
        public override void CreateEventSchemeForLayout()
        {
            if (pClientLayoutMini != null)
            {
                pClientLayoutMini.ID.ClearEvent();
                pClientLayoutMini.ID.PressF1 += new EventHandler(body.Help);
            }
        }

        /// Привязка данных
        public override void CreateBinding()
        {
            if (pClientLayout != null)
            {
                pClientLayout.Name.SetBinding(TextBoxExt.ValueProperty, new BindingExt("Name"));
                pClientLayout.Address.SetBinding(TextBoxExt.ValueProperty,
                    new BindingExt("Address.Address"));
                pClientLayout.RegistrationNumber.SetBinding(TextBoxExt.ValueProperty,
                    new BindingExt("RegistrationNumber"));
                pClientLayout.TaxPayerCode.SetBinding(TextBoxExt.ValueProperty,
                    new BindingExt("TaxPayerCode"));
            }
            if (pClientLayoutMini != null)
            {
                pClientLayoutMini.PersonName.SetBinding(LabelExt.ContentProperty,
                    new BindingExt("Name"));
                pClientLayoutMini.ID.SetBinding(TextBoxExt.ValueProperty,
                    new BindingExt("PIN"));
            }
        }
        public override void RefreshDataContext(object argClient)
        {
            // Обновление на экране
            ActiveLayout.DataContext = argClient;
        }
    }


    /// Компонент "Клиент"
    public class ClientComponent : BusinessComponent, IHelper
    {
        #region IHelper realization
        /// Поиск клиента по PIN
        public virtual void Find(object sender, EventArgs e)
        {
            ...
        }
        /// Показ помощи по клиентам по F1
        public virtual void Help(object sender, EventArgs e)
        {
             ...
        }
        /// признак, был ли найден объект
        public bool IsFound
        {
            get { return isFound; }
            set { isFound = value; }
        }
        /// признак, был ли найден объект
        private bool isFound = true;
        #endregion


        #region Fields
        /// Данные о клиенте
        private IClient pClient;
        #endregion

        #region Properties
        /// Данные о клиенте
        public IClient Client
        {
            get { return pClient; }
            set
            {
                if (pClient != value)
                {
                    pClient = value;
                    OnPropertyChanged("Client");
                }
            }
        }
        #endregion

        #region  Constructor
        /// Конструктор
        /// <param name="argClient">Интерфейс "Клиент"</param>
        public ClientComponent(IClient argClient)
        {
            pClient = argClient;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Обновить данные визуального компонента у которого уже есть контроллер,
        /// данными от невизуального компонента
        /// </summary>
        /// <param name="argNewValue">невизуальный компонент, данные которого нужно
        /// присоединить</param>
        protected override void AssignRealization(BusinessComponent argNewValue)
        {
            Client = ((ClientComponent)argNewValue).Client;
            Refresh(Client);
        }
        #endregion
    }

/////////////////////////////////////////////////////////////////////////////////////////////////////

    //--------- В бизнес-сущности или компоненте на уровень выше -----------------------------------------

    /// партнер по сделке (body)
    private ClientComponent Beneficiary;

    ClientComponent locPartner = new ClientComponent((IClient)new Institution());
    locPartner.Client.PIN = (string)locResultRow["ClCode"];
    locPartner.Client.Name = (string)locResultRow["PartnrName"];
    Beneficiary.Assign(locPartner);

    //--------- В главном контроллере или контроллере компонента на уровень выше -------------------------

    private ClientController BeneficiaryController;

    // pLayout
    <C:InstitutionLayout Grid.Row="3" x:Name="Beneficiary"/>

    BeneficiaryController = new ClientController(body.Beneficiary, pLayout.Beneficiary);
    AddSubController(BeneficiaryController);


    //----------------------------------------------------------------------------