Варианты дизайна класса с использованием безопасного даункинга - PullRequest
0 голосов
/ 07 сентября 2011

После борьбы с понижением (см. [мой оригинальный пост] ) и с созданием [глубоких копий] я нашел [эту одноименную статью] гдеВ C ++ сделано предложение о том, как бороться с этой проблемой.С большим волнением я реализовал это в C # следующим образом:

public partial class User
{
virtual public Employer GetEmployer() { return null; }
...
}

public partial class Employer
{
public override Employer GetEmployer() { return this; }
...
}

, который я затем использовал следующим образом:

User u = GetUser();
Employer e = u.GetEmployer();

Однако (я думаю, что неудивительно), переопределение никогда не вызываетсяи возвращается ноль.


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

В этом конкретном случае я получаю адрес электронной почты от кого-то, использующего мой веб-сайт, так что я знаю, что они User, но в целом я ничего о них не знаю.Позже (когда они заполняют форму), я знаю, что они на самом деле Employer, поэтому мне нужно преуменьшить мою User.

Какой здесь правильный подход?

Ответы [ 3 ]

1 голос
/ 07 сентября 2011

Я ответил на один из твоих прежних вопросов.Вы пытаетесь решить то, что не может быть решено.Решение здесь не в том, чтобы использовать наследование.Почему, потому что наследование - это is a отношение.Так что если у вас есть:

public class User { }

public class Employee : User { }

У вас есть это отношение Employeee is User, но у вас нет обратное отношение User is Employee.Но это именно то, что вы пытаетесь сделать с кастом от User до Employee.Экземпляр User нельзя привести к Employee (кроме ситуации, о которой я упоминал в предыдущем ответе - но у вас нет такой ситуации).

Используйте этот подход, и вы решите его в объектно-ориентированномпуть без потребности в наследовании или что-либо еще.

public class User 
{
    public virtual EmployeeData { get; set; }
}

public class EmployeeData { }

Подход меняет ваш дизайн с is a на has a отношения.EmployeeData в этом случае отдельная сущность отображается в отношении 1 - 0..1 (что приведет к двум таблицам в базе данных).Вы также можете использовать 1 - 1 отношение или ComplexType, если вас устраивает тот факт, что User и EmployeeData будут храниться в одной таблице.

0 голосов
/ 07 сентября 2011

Вы должны следовать шаблону состояния :

Это чистый способ частичного изменения своего типа во время выполнения.

Затем вы можете смешивать и сочетать с Образцом прототипа :

[Использовать] прототипный экземпляр, который клонируется для создания новых объектов.

Таким образом, вы могли бы получить что-то вроде этого:

// State pattern: public "wrapper"
public class User {
    UserState state = UserState.CreateUser();

    public void SetIsEmployer ()
    {
        // Use UserState.IsEmployer() method to transition to the
        // Employer state
        state = state.IsEmployer ();
    }

    public User Employer {
        get {return state.Employer.User;}
    }
}

// State pattern: User state
internal class UserState {
    // protected so that only CreateUser() can create instances.
    protected UserState ()
    {
    }

    public User User {
        get {/* TODO */}
    }

    public virtual UserState Employer {
        get {return null;}
    }

    // Creates a default prototype instance
    public static UserState CreateUser ()
    {
         return new UserState ();
    }

    // Prototype-ish method; creates an EmployerState instance
    public virtual UserState IsEmployer ()
    {
        return new EmployerState (/* data to copy...*/);
    }
}

// State pattern: Employer state
class EmployerState : UserState {
    internal EmployeeState ()
    {
    }

    public override UserState Employer {
        get {return this;}
    }
}

Если вашему общедоступному типу User требуется больше «переходов состояний», вам нужно только предоставить больше классов (по одному для каждого состояния) и методов prototype-ish для типа UserState, чтобы создать соответствующий тип целевого состояния. User.state всегда является текущим состоянием User. Эта настройка позволяет вам изменять видимый тип времени выполнения экземпляра во время выполнения.

0 голосов
/ 07 сентября 2011

Единственная недостающая часть вашего примера - это то, что работодатель не наследует от пользователя.

Измените вашу декларацию на:

public partial class Employer : User
{
    // ...
}

И вам следует идти дальше.Я не уверен, что пошел бы этим путем все же.Возможно, я бы просто использовал ключевое слово as для безопасного приведения:

var user = GetUser();
var employer = user as Employer;

// if the cast failed, employer will be null
if(employer != null)
{
    // Work with employer
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...