Представление бизнес-сущности в качестве компонента
Проблематика
[править]Если мы программируем некую цельную систему, мы достаточно быстро заметим, что сложные бизнес-сущности состоят из частей, которые ссылаются на более простые бизнес-сущности. Так например, в описании платежа мы встретим такие понятия как клиент, различные банки, пользователи этой системы. В платеже они будут представлены компонентом, состоящем из минимальной идентификации соответствующей сущности. Так, например, о банке нам будет интересно только наименование, адрес, swift, и еще немногое число его свойств. При этом в системе конечно же существует справочник в котором содержится более полная информация обо всех используемых банках. При этом в этом случае информация о банке будет визуально представлена т.н. карточкой, в которой будет уже порядка 30-50 различных свойств о банке.
Таким образом, у нас появляется задача так запрограммировать систему, чтобы избежать некоторого дублирования данных. А также уметь работать с бизнес-сущностями, классифицируя их исходя из разных точек зрения. Здесь мы рассмотрим один пример.
По сути мы имеем две иерархии:
- бизнес-сущностей: лицо, физическое лицо, юридическое лицо и банк
- компонентов: банк, клиент и пользователь
При этом когда мы используем компоненты, мы частично используем бизнес-сущности. При этом это может быть не один в один, а по достаточно своеобразной логике. Наиболее просто связана бизнес-сущность (карточка) банка с компонентом банк - тут связь один в один. Но тем не менее уже тут описано, что банк это частный случай юридического лица, которое в свою очередь частный случай лиц. А компонент представляет собой лишь частный случай бизнес-сущности банк, интересуясь 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);
//----------------------------------------------------------------------------