Відображення із наслідуванням (шаблон проєктування)

Відображення із наслідуванням (англ. Inheritance Mappers) — шаблон проєктування, який описую структуру класів відображення ієрархії наслідування.

Опис ред.

При роботі із ієрархією об'єктів, які реалізують наслідування, необхідно зменшити об'єм коду, який використовується для запису і читання зі сховища, а також надати абстрактні та конкретні методи для роботи із даними.

Не зважаючи на те який спосіб наслідування реалізований зі сховищем (або образу декілька) опишемо структуру яка однакова для всіх.

Реалізація ред.

 

Нехай дана ієрархія об'єктів.

public class Player
{
    public string Name { get; set; }
}

class Footballer : Player
{
    public string Club { get; set; }
}

class Cricketer : Player
{
    public int BattingAverage { get; set; }
}

Опишемо абстрактний клас для відображення зі структури в пам'яті до доменного об'єкта.

internal class AbstractMapper
{       
    public object Find(int id)
    {
        var row = FindRow(id);
        var domainObject = CreateDomainObject();
        return PopulateDomainObject(row, domainOjbect);
    }

    public void Insert(object domainObject)
    {            
        var dataObject = CreateDataObject();
        var row = PopulateTableObject(domainObject, dataObject);

        db.Insert(row);
    }

    public void Update(object domainObject)
    {
        var id = GetId(domainObject);
        var dataObject = FindRow();
        var row = PopulateTableObject(domainObject, dataObject);

        db.Update(row);
    }

    public void Delete(object domainObject)
    {
        var row = FindRow(id);
        db.Delete(row);
    }

    protected abstract object GetId(object domainObject);
    protected abstract object FindRow(int id);
    protected abstract object CreateDomainObject();
    protected abstract object CreateDataObject();
    protected abstract object PopulateDomainObject(object row, object domainObject);
    protected abstract object PopulateTableObject(object domainObject, object tableObject);
}

Кожний клас ієрархії повинний реалізувати свою логіку відображення.

internal class PlayerMapper : AbstractMapper
{
    protected object FindRow(int id)
    {
        return db.Find(id) as PlayerTable;
    }
    protected object CreateDomainObject(int id)
    {
        return new Player();
    }
    protected object PopulateDomainObject(object row, object domainObject)
    {
        var playerDataRow = row as PlayerTable;
        var playerDomainObject = domainObject as Player;

        playerDomainObject.Name = playerDataRow.Name;

        return playerDomainObject;
    }
         . . .
}

Класи відображення наслідують у тому ж порядку, що і оригінальна ієрархія та при потребі перевикористовують логіку.

internal class FootballerMapper : PlayerMapper
{
    protected override object PopulateDomainObject(object row, object domainObject)
    {
        var footballerDataRow = base.PopulateDomainObject(row, domainObject) as FootballerMapper;

        footballerDataRow.Name = (row as FootballerTable).Club;

        return footballerDataRow;
    }
         . . .
}

Заховаємо вищеописані класи за фасадом, який буде доступний користувачу.

class Mapper
{
    private Dictionary<Type, AbstractMapper> mappers = new Dictionary<Type, AbstractMapper>();

    // метод реєстрації класів відображення
    public void RegisterMapper(Type type, AbstractMapper mapper)
    {
        mappers[type] = mapper;
    }

    private AbstractMapper GetMapper(object domainObject)
    {
        return mappers[domainObject.GetType()];
    }

    public object Find(int id)
    {
        foreach (var mapper in mappers.Values)
        {
            var domainObject = mapper.Find(id);

            if (domainObject != null) return domainObject;
        }
    }

    public void Insert(object domainObject)
    {      
        // делегуємо роботу відповідним класам відображення      
        GetMapper(domainObject).Insert(domainObject);
    }

    public void Update(object domainObject)
    {
        GetMapper(domainObject).Update(domainObject);
    }

    public void Delete(object domainObject)
    {
        GetMapper(domainObject).Delete(domainObject);
    }
}

Див. також ред.

Джерела ред.