Проектирование...
От: white_znake  
Дата: 22.07.10 16:47
Оценка:
Здравствуйте, уважаемые коллеги

Есть классы:

public class BaseEntity
{
}

public class Entity1 : BaseEntity
{
}

public class Entity2 : BaseEntity
{
}

public interface IBaseModel<T> where T : BaseEntity
{
}

public class Model1 : IBaseModel<Entity1>
{
}

public class Model2 : IBaseModel<Entity2>
{
}


Нужна фабрика, которая бы в зависимости от входного параметра, создавала бы нужный экземпляр Model1, Model2 без приведения типов...
Re: Проектирование...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.07.10 17:26
Оценка: +1
Здравствуйте, white_znake, Вы писали:

_>Нужна фабрика, которая бы в зависимости от входного параметра, создавала бы нужный экземпляр Model1, Model2 без приведения типов...


Что значит без приведения типов?
У Model1 и Model2 ближайший общий предок — System.Object. Метод, который может вернуть и Model1 и Model2 должен возвращать System.Object.
Re: Проектирование...
От: _FRED_ Черногория
Дата: 22.07.10 19:38
Оценка:
Здравствуйте, white_znake, Вы писали:

_>Нужна фабрика, которая бы в зависимости от входного параметра, создавала бы нужный экземпляр Model1, Model2 без приведения типов...


Рассказывайте, что не устраивает в следующем методе и будем исправлять.
public object CreateModel(object parameter) {
  if(IsNeedModel1(parameter)) {
    return new Model1();
  } else if(IsNeedModel2(parameter)) {
    return new Model2();
  }//if

  return null;
}
Help will always be given at Hogwarts to those who ask for it.
Re[2]: Проектирование...
От: Klapaucius  
Дата: 23.07.10 11:06
Оценка:
Здравствуйте, samius, Вы писали:

S>Что значит без приведения типов?

S>У Model1 и Model2 ближайший общий предок — System.Object. Метод, который может вернуть и Model1 и Model2 должен возвращать System.Object.

На самом деле не должен, конечно. Он может возвращать тип сумму Model1 + Model2. Т.е. Either<Model1,Model2>. И можно представить себе фабрику, которая возвращает сумму моделей, принимая параметром сумму энтити, например. Понятно, что множество наследников открыто, а сумма закрыта, ну так это может даже быть полезным.
А еще можно представить дженерик-фабрику, тип производимой модели для которой будет определятся в (jit)компайл-тайм. И тоже без приведений моделей к общему типу.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[3]: Проектирование...
От: _FRED_ Черногория
Дата: 23.07.10 11:28
Оценка:
Здравствуйте, Klapaucius, Вы писали:

S>>Что значит без приведения типов?

S>>У Model1 и Model2 ближайший общий предок — System.Object. Метод, который может вернуть и Model1 и Model2 должен возвращать System.Object.

K>На самом деле не должен, конечно. Он может возвращать тип сумму Model1 + Model2. Т.е. Either<Model1,Model2>. И можно представить себе фабрику, которая возвращает сумму моделей, принимая параметром сумму энтити, например. Понятно, что множество наследников открыто, а сумма закрыта, ну так это может даже быть полезным.


Ага, а вот как использовать-то это добро? такой Either по сути ничем не будет от object-а отличаться, достаточно Either::IsFirst заменить на is Model1

K>А еще можно представить дженерик-фабрику, тип производимой модели для которой будет определятся в (jit)компайл-тайм.


Что значат скобочки вокруг (jit)?

K>И тоже без приведений моделей к общему типу.


И как с такими моделями работать? И как оно будет выглядеть в шарпе (интерфейс хотя бы)?
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Проектирование...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.07.10 12:16
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, Klapaucius, Вы писали:


K>>На самом деле не должен, конечно. Он может возвращать тип сумму Model1 + Model2. Т.е. Either<Model1,Model2>. И можно представить себе фабрику, которая возвращает сумму моделей, принимая параметром сумму энтити, например. Понятно, что множество наследников открыто, а сумма закрыта, ну так это может даже быть полезным.


_FR>Ага, а вот как использовать-то это добро? такой Either по сути ничем не будет от object-а отличаться, достаточно Either::IsFirst заменить на is Model1


Either позволит формально обойтись без приведения/проверки типа.
Re[3]: Проектирование...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.07.10 12:18
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>Здравствуйте, samius, Вы писали:


K>На самом деле не должен, конечно. Он может возвращать тип сумму Model1 + Model2. Т.е. Either<Model1,Model2>. И можно представить себе фабрику, которая возвращает сумму моделей, принимая параметром сумму энтити, например. Понятно, что множество наследников открыто, а сумма закрыта, ну так это может даже быть полезным.


По поводу суммы/Either согласен, но это уже не характерно для .NET-а.

K>А еще можно представить дженерик-фабрику, тип производимой модели для которой будет определятся в (jit)компайл-тайм. И тоже без приведений моделей к общему типу.


Можно подробнее?
Re[4]: Проектирование...
От: Klapaucius  
Дата: 23.07.10 12:25
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>такой Either по сути ничем не будет от object-а отличаться, достаточно Either::IsFirst заменить на is Model1


Одно принципиальное отличие я, в общем-то, уже называл. Either — закрытый, так что там может быть (например) или Model1 или Model2, а под маской object-а может быть что угодно. Но есть и еще отличия. Either мог бы быть сделан так, чтобы было невозможно получить Model1 без предварительной проверки, что он там есть. А object можно просто даункастить к Model1 — писать проверку is Model1 никто не заставляет. Но не в шарпе, конечно, кастить Either к Left<Model2> не запретишь.

_FR>Что значат скобочки вокруг (jit)?


Ничего особенного. Это просто примечание, что под компайл-таймом имеется в виду компиляция джитом, а не в байт-код.

_FR>И как с такими моделями работать? И как оно будет выглядеть в шарпе (интерфейс хотя бы)?


//фабрика ничего не знает про IAnotherModelMethod
public delegate TM ModelFactory<out TM, in TE>(TE entity)
    where TM : IBaseModel<TE>
    where TE : BaseEntity;

public class FactoryUser
{
    public static void UseEntity<TM, TE>(ModelFactory<TM, TE> factory, TE entity)
        where TM : IBaseModel<TE>, IAnotherModelMethod //a UseEntity знает
        where TE : BaseEntity
    {
        var model = factory(DoSomething(entity));
        model.Foo(); //метод IBaseModel
        model.BlahBlahBlah(); //метод IAnotherModelMethod
    }
}
//где-то в другой сборке:
FactoryUser.UseEntity(e => new Model1(DoSomething(e)), new Entity1());


Я, кстати, не стану спорить, если кто-то скажет, что все это проходит по разряду извращений: шарп — явно не тот язык, на котором можно многое написать не прибегая к даункастам. Придется продираться через тернии угловых скобочек пока не обнаружится, что путь к звездам перегорожен отсутствием higher-kinded полиморфизма или еще чем-нибудь. Так что возвращать object из фабрики и кастить — это вполне нормальный С#-way.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[5]: Проектирование...
От: _FRED_ Черногория
Дата: 23.07.10 13:24
Оценка:
Здравствуйте, samius, Вы писали:

K>>>На самом деле не должен, конечно. Он может возвращать тип сумму Model1 + Model2. Т.е. Either<Model1,Model2>. И можно представить себе фабрику, которая возвращает сумму моделей, принимая параметром сумму энтити, например. Понятно, что множество наследников открыто, а сумма закрыта, ну так это может даже быть полезным.


_FR>>Ага, а вот как использовать-то это добро? такой Either по сути ничем не будет от object-а отличаться, достаточно Either::IsFirst заменить на is Model1


S>Either позволит формально обойтись без приведения/проверки типа.


А этого можно добиться очень многими способами. Функционально-то что было, то и есть: результат нужно проверить, а потом применить некую функцию от результата, что бы добраться до самой модели.
Help will always be given at Hogwarts to those who ask for it.
Re[5]: Проектирование...
От: _FRED_ Черногория
Дата: 23.07.10 13:37
Оценка:
Здравствуйте, Klapaucius, Вы писали:

_FR>>такой Either по сути ничем не будет от object-а отличаться, достаточно Either::IsFirst заменить на is Model1


K>Одно принципиальное отличие я, в общем-то, уже называл. Either — закрытый, так что там может быть (например) или Model1 или Model2, а под маской object-а может быть что угодно.


"Закрытость" object-а здесь гарантирует реализация фабрики. И в том object-е может быть ровно то же, что и в Either-е, то есть в данном случае существует взаимно-однозгначное преобразование object->Either. Но преимущества для пользователя никакого не будет — он как не мог ничего вызывать от результата работы фабрики, так и не может, а вот узнать что именно вернулось может в любом слачае.

K>Но есть и еще отличия. Either мог бы быть сделан так, чтобы было невозможно получить Model1 без предварительной проверки, что он там есть. А object можно просто даункастить к Model1 — писать проверку is Model1 никто не заставляет. Но не в шарпе, конечно, кастить Either к Left<Model2> не запретишь.


А даункаст это разве не та же проверка? Если цимес в том, что "не та же" всмысле более контролируема, то в данном случае топикстартеру от этого ни тепло ни холодно. Наоборот: имея object он сможет передать его, например, куда-то для биндинга/сериализации или чего-ещё, а вот над either-он надо ещё будет для этого потрясти бубном.

_FR>>И как с такими моделями работать? И как оно будет выглядеть в шарпе (интерфейс хотя бы)?

K>//фабрика ничего не знает про IAnotherModelMethod
K>public delegate TM ModelFactory<out TM, in TE>(TE entity)
K>    where TM : IBaseModel<TE>
K>    where TE : BaseEntity;


Это уже совсем "не фабрика" в контексте топика, где требуется создать объект в зависимости от.

K>Я, кстати, не стану спорить, если кто-то скажет, что все это проходит по разряду извращений: шарп — явно не тот язык, на котором можно многое написать не прибегая к даункастам.


Да нет, довольно много можно. Проблема в том, что из-за BCL/FCL часто без даункастов никак. С хорошим фреймворком можно свести даункасты к минимуму.

K>Придется продираться через тернии угловых скобочек пока не обнаружится, что путь к звездам перегорожен отсутствием higher-kinded полиморфизма или еще чем-нибудь. Так что возвращать object из фабрики и кастить — это вполне нормальный С#-way.


Нет, нормальный way — это описание нормального (для [скромных] возможностей шарпа) интерфейса модели. В данном примере топикстартер просто поленился показать то, каким образом он собирается (или ему хотелось бы) использовать свои энтити и модели — в таком разрезе ничего конкретного посоветовать нельзя.
Help will always be given at Hogwarts to those who ask for it.
Re[6]: Проектирование...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.07.10 15:36
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, samius, Вы писали:


K>>>>На самом деле не должен, конечно. Он может возвращать тип сумму Model1 + Model2. Т.е. Either<Model1,Model2>. И можно представить себе фабрику, которая возвращает сумму моделей, принимая параметром сумму энтити, например. Понятно, что множество наследников открыто, а сумма закрыта, ну так это может даже быть полезным.


_FR>>>Ага, а вот как использовать-то это добро? такой Either по сути ничем не будет от object-а отличаться, достаточно Either::IsFirst заменить на is Model1


S>>Either позволит формально обойтись без приведения/проверки типа.


Вот тут я лопухнул. Смутило меня Either::IsFirst. Если плясать от хаскеля, то соответствующая C#-у конструкция будет выглядеть как ... is Left.

_FR>А этого можно добиться очень многими способами. Функционально-то что было, то и есть: результат нужно проверить, а потом применить некую функцию от результата, что бы добраться до самой модели.


Да, можно взять Tuple<Model1, Model2> и проверять его компоненты на null.
Re[6]: Проектирование...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 23.07.10 15:40
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Нет, нормальный way — это описание нормального (для [скромных] возможностей шарпа) интерфейса модели. В данном примере топикстартер просто поленился показать то, каким образом он собирается (или ему хотелось бы) использовать свои энтити и модели — в таком разрезе ничего конкретного посоветовать нельзя.


Действительно, если нет хоть какого-то полиморфного поведения, смысла засовывать разнотипные объекты в одну фабрику не много.
Re: Проектирование...
От: mrTwister Россия  
Дата: 24.07.10 11:18
Оценка:
Здравствуйте, white_znake, Вы писали:


_>Нужна фабрика, которая бы в зависимости от входного параметра, создавала бы нужный экземпляр Model1, Model2 без приведения типов...


Возвращать из фабрики нешаблонный интерфейс IBaseModel
лэт ми спик фром май харт
Re[6]: Проектирование...
От: Klapaucius  
Дата: 26.07.10 11:01
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>"Закрытость" object-а здесь гарантирует реализация фабрики.


Даже если фабрика конкретная — о каких-то гарантиях говорить сложно — они ведь явно в виде интерфейса не выражены. Если же фабрика абстрактна — тут все "гарантии" и вовсе вилами по воде писаны.

_FR>И в том object-е может быть ровно то же, что и в Either-е, то есть в данном случае существует взаимно-однозгначное преобразование object->Either. Но преимущества для пользователя никакого не будет — он как не мог ничего вызывать от результата работы фабрики, так и не может, а вот узнать что именно вернулось может в любом слачае.


У пользователя одинаковые возможности для того, чтобы определить вернулось ли то, что он ожидает. Но что он может ожидать от object? Да чего угодно. Если в случае суммы типов — все возможные возвращаемые модели в интерфейсе перечислены, то какая есть возможность узнать, что возвращает фабрика в обджекте? Посмотреть ее исходный код? Испытать на всех возможных входных данных? Все-таки тип — это автоматически проверяемая и всегда актуальная документация.

_FR>имея object он сможет передать его, например, куда-то для биндинга/сериализации или чего-ещё, а вот над either-он надо ещё будет для этого потрясти бубном.


Это, конечно, верно. Either, правда, может иметь преобразование к object.

_FR>Это уже совсем "не фабрика" в контексте топика, где требуется создать объект в зависимости от.


Требовалось создавать объект определенного типа в зависимости от параметра. В данном случае — в зависимости от параметра типа. Это просто вторая попытка домысливания вопроса до того, чтобы в нем появился хоть какой-то смысл.

_FR>Да нет, довольно много можно. Проблема в том, что из-за BCL/FCL часто без даункастов никак. С хорошим фреймворком можно свести даункасты к минимуму.


Полагаю, что BCL/FCL такая не от хорошей жизни. Конечно, там могут быть неоправданно плохие решения, но по большому счету проблемы библиотеки — прямое следствие недостатков в системе типов CLR. Проблема в том, что фактически из триады (полиморфный код, иммутабельные объекты, отсутствие даункастов) можно выбрать только два пункта. При чем тут иммутабельность объектов? Ну, у них много "замкнутых" операций. Отображающих из множества объектов в то же множество. Тут мы впервые сталкиваемся с потерей информации о типах, которое я описал здесь
Автор: Klapaucius
Дата: 13.07.10
. Там же написано как это обходить (с дополнительными проблемами в виде нагромождения параметров типов, "загрязнения" простаранства имен и списка автодополнения, а также проблем с выведением параметров типов, но для части из этих проблем тоже есть решения), но обходится это до поры, пока не понадобятся операции вроде Select, которые из типа C<T> делают тип C<V>. Все, тут без даункастов уже не обойтись, потому как higher-kinded полиморфизм отсутствует. Да и ко/контравариантность для классов потом понадобится, а не только для интерфейсов и делегатов. В BCL/FCL проблема работы с коллекциями без их изменения по месту еще относительно удачно решена — там есть итераторы-монады (ну и функторы, понятное дело), которые комбинируются, а потом из них какой-нибудь ToList или ToDictionary строит окончательную, нужную нам, структуру данных. Но итераторы имеют только минимальный набор операций, информации о типе источника не имеют, так что на практике полиморфный код дается ценой ухудшения асимптотики алгоритмов в ряде случаев. Поэтому в абстракциях проиходится все теми же даункастами пробивать дыры. За примерами далеко ходить не надо, тот же Enumerable.Count содержит внутри хак:
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
    return is2.Count;
}

Получается, что экстеншн для IEnumerable знает о существовании ICollection. Просто замечательно.

_FR>Нет, нормальный way — это описание нормального (для [скромных] возможностей шарпа) интерфейса модели.


Тут принципиальной разницы и нет — апкаст к интерфейсу или к object — это все один способ написания полиморфного кода и даункаст при таком подходе вполне штатное и широко используемое средство. Ну а то, что это падает в рантайме и плохо проверяется компилятором — ну так ООП вообще пришел из динамики. Там падение в рантайме — это и есть единственная возможная проверка.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re: Проектирование...
От: Аноним  
Дата: 26.07.10 20:12
Оценка:
_>Нужна фабрика, которая бы в зависимости от входного параметра, создавала бы нужный экземпляр Model1, Model2 без приведения типов...

Взаимоисключающие требования: в зависимости от входного параметра — значит, динамически, без приведения типа — значит, статически.

Если ты не собираешься использовать dynamic, задача не имеет смысла.
Re[2]: Проектирование...
От: samius Япония http://sams-tricks.blogspot.com
Дата: 26.07.10 20:48
Оценка:
Здравствуйте, Аноним, Вы писали:

_>>Нужна фабрика, которая бы в зависимости от входного параметра, создавала бы нужный экземпляр Model1, Model2 без приведения типов...


А>Взаимоисключающие требования: в зависимости от входного параметра — значит, динамически, без приведения типа — значит, статически.

Можно обойтись без приведений имея выходным результатом кортеж.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.