Oggetto dominio "Contragent"
Diciamo che ho una gerarchia di classi:
public class BaseContragent
{
public int Id { get; set; }
}
public class PersonContragent : BaseContragent
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class CompanyContragent : BaseContragent
{
public string CompanyName { get; set; }
}
public class BankCompanyContragent : CompanyContragent
{
public string BankBic { get; set; }
}
public class LocalBankCompanyContragent : BankCompanyContragent
{
public string NationalBankIdentificator { get; set; }
}
In realtà, questa gerarchia ha molte classi, ha molta logica aziendale e tutti i campi sono presi dalla logica del dominio aziendale, quindi non posso cambiarla solo a causa di un servizio esterno scomodo.
Contragent
- PersonContragent
-- PersonWithoutCitizenshipContragent
-- CitizenshipContragent
--- LocalCitizenshipContragent
--- ForeignCitizenshipContragnet
- CompanyContragent
-- BankCompanyContragent
--- LocalBankCompanyContragent
etc.
Ora, il sistema esterno invia richieste per creare oggetto
Ho una richiesta per creare un oggetto da un sistema esterno, che assomiglia a questo:
public class CreateRequest
{
public int Id { get; set; }
public string Name { get; set; } // default for base, value for subclasses
public int Type { get; set; } // 0 - for Base, 1 for Person, 2 for Company
// many other fields belonging to different types
}
Esiste un solo tipo di richiesta di creazione per tutti i tipi di Contragent
, e quale dovrei utilizzare dipende dal valore Type
.
Per isolare il mio sistema da questo strano sistema esterno ho implementato una sorta di strato anti-corruzione con le fabbriche che creano oggetti di questa gerarchia:
public abstract class BaseContragentFactory
{
protected abstract Contragent Create();
protected abstract Fill(Contragent ca, CreateRequest createRequest);
public Base CreateAndFill(CreateRequest createRequest)
{
var ca = Create();
Fill(ca, createRequest);
return ca;
}
}
public class SimpleContragentFactory : BaseContragentFactory
{
protected virtual Contragent Create()
{
return new Contragent();
}
protected virtual void Fill(Contragent ca, CreateRequest createRequest)
{
ca.Id = createRequest.Id;
}
}
public class PersonContragentFactory : SimpleContragentFactory
{
protected override Contragent Create()
{
return new PersonContragent();
}
protected override void Fill(Contragent ca, CreateRequest createRequest)
{
var pca = ca as PersonContragent;
if (pca == null)
throw new InvalidOperationException("...");
base.Fill(pca, createRequest);
string[] nameParts = createRequest.Name.Split(";"); // Firstname;Lastname
pca.FirstName = nameParts[0];
pca.LastName = nameParts[1];
}
}
public class CompanyContragentFactory : SimpleContragentFactory
{
protected override Contragent Create()
{
return new CompanyContragent();
}
protected override void Fill(Contragent ca, CreateRequest createRequest)
{
var bca = ca as CompanyContragent;
if (bca == null)
throw new InvalidOperationException("...");
base.Fill(bca, createRequest);
bca.CompanyName = createRequest.Name;
}
}
Ho fabbriche del genere per quasi tutti i tipi di Contragent
.
public void Create(CreateRequest request)
{
BaseContragentFactory factory;
switch (request.Type)
{
case 0:
factory = new SimpleContragentFactory();
break;
case 1:
factory = new PersonContragentFactory();
break;
case 2:
factory = new CompanyContragentFactory();
break;
default:
throw new InvalidOperationException("...");
}
Contragent ca = factory.CreateAndFill(request);
}
Tuttavia, non mi piace che questo codice abbia cast, controlli e che la sicurezza del tipo sia mantenuta solo da uno sviluppatore.
Quindi, ho due domande su come migliorare la sicurezza e la leggibilità del tipo di codice:
Domanda 1: come combinare i metodi Create
e Fill
? Come rendere questa classe avere un solo metodo. Il problema è che devo essere in grado di creare documenti solo una volta nella classe ereditata dall'alto e quindi chiamare i metodi di riempimento base
.
Domanda 2: esiste un modo per utilizzare i generici per rendere queste fabbriche più sicure dal punto di vista dei tipi e accettare valori generici tipizzati? Può essere, qualcosa del genere:
public abstract class BaseContragentFactory<T> where TContragent : Contragent, new()
{
protected TContragent Create()
{
return new TContragent();
}
// ...