Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Общие вопросы по .NET и C# > спроектировать класс


Автор: 17dufa 11.3.2010, 14:31
задача простая до безобразия, а я туплю:
есть заказ, есть позиции в заказе. задача - написать структуру классов таким образом, чтобы позицию нельзя было создать вне заказа.
мои варианты:
1. запихнуть это в сборку, конструктор позиции internal, у заказа есть public метод AddPosition, который уже создаст позицию. Но есть одна жопа: если класс позиции бум часто менять постоянно придется менять 3 части: сам класс позиции, метод AddPosition, клиента, который вызывает AddPosition. 1 и 3 части неизбежны, а вот изменения в самом методе AddPosition вроде как бы неплохо выкинуть. но как? создать еще один класс (точнее наверно структуру) с названием Данные_Позиции. клиент ее заполняет, передает в AddPosition, а тот передает в конструктор позиции? тогда изменения будут касаться только этой структуры и метод AddPosition менять не придется.
2. в конструкторе позиции передавать заказ, в который она входит. дальше встает вопрос - а как заказ узнает о своей позиции? делать в заказе метод типа RegisterPosition и обязывать конструктор позиции его вызывать? во-первых кривовато, во-вторых встает задача как спрятать сам метод RegisterPosition, чтоб его не вызывали кто не попадя.
3. можно подумать что-то насчет вложенных классов, но чего именно подумать не придумывается.

Автор: uranpro 11.3.2010, 16:45
не совсем понял, но... =)
Код

    public class Zakaz
    {
        public class Pos
        {
            public int ID { get; set; }
            private Pos(int ID)
            {
                this.ID = ID;
            }

            static protected internal Pos GetNewInstance(int ID)
            {
                return new Pos(ID);
            }
        }

        public List<Pos> Positions = new List<Pos>();

        public void AddPos(int ID)
        {
            Positions.Add(Pos.GetNewInstance(ID));
        }

    }

Автор: 17dufa 11.3.2010, 17:13
uranpro, теперь представим, что Pos у нас крайне неустойчивый класс, то добавится поле, то тип поменяет. придется менять:
1. сам класс Pos
2. метод Zakaz::AddPos
3. клиентский код, который юзает Zakaz::AddPos
можно пункт 2 исключить?

Автор: uranpro 11.3.2010, 17:24
17dufa, я показал как использовать приватные конструкторы и как пользоваться модификатором доступа "protected internal".
вопрос был такой, насколько я понял: как сделать так, чтобы экземпляры позиций можно было создать только в экземпляре заказа =)
мб так
Код

    public class Zakaz
    {
        public class Pos
        {
            public int ID { get; set; }
            private Pos() { }
            static protected internal Pos GetNewInstance()
            {
                return new Pos();
            }
        }

        public List<Pos> Positions = new List<Pos>();

        public Pos NewPos()
        {
            return Pos.GetNewInstance();
        }
    }


Добавлено через 2 минуты и 36 секунд
Код

var z = new Zakaz();
            Zakaz.Pos p = z.NewPos();
...
Zakaz.Pos p = new Zakaz.Pos();// ошибка

Автор: 17dufa 11.3.2010, 17:55
uranpro, да, этот вариант решает поставленную задачу. так же как и вариант с internal в отдельной сборке и как вариант с передачей экземпляра класса заказа в конструктор позиции. но этот вариант имеет некоторый недостаток, на который мне было указано, когда я утром отвечал на данный вопрос на собеседовании, а именно:
что будет, если класс заказа будет меняться? будет собственно говоря не очень хорошая штука: придется слишком много кода править. можно это обойти?
кстати, придумал как можно:
Код

Zakaz z = new Zakaz();
z.NewPos().Init(/*необходимые параметры, меняй скока хошь*/);

правда решая проблему изменчивости класса заказа, такое решение порождает более серьезную проблему: во-первых, объект позиции выходит из конструктора в несколько противоречивом состоянии, во-вторых, нет способа заставить клиента вызвать метод Init, то есть вполне возможна ситуация:
Код

Zakaz z = new Zakaz();
z.NewPos(); /*а Init никто и не вызвал*/

Автор: uranpro 11.3.2010, 18:37
17dufa, не пойму в чем проблема, меняется класс заказа, а не позиции =( а если использовать перегрузку ?

Автор: 17dufa 11.3.2010, 19:23
uranpro, вот смотри допустим у нас в позиции хранится тока стоимость, будет что-то типа:
Код

double cost = ....
Zakaz.Pos p = z.NewPos(cost);

потом мы подумали и решили, что не плохо б еще сохранить ссылку на товар, получается:
Код

double cost = ....
Tovar t = ....
Zakaz.Pos p = z.NewPos(cost, t);

потом бац, нафиг цену, в товаре есть цена за единицу, бум хранить количество:
Код

int kolvo = ....
Tovar t = ....
Zakaz.Pos p = z.NewPos(kolvo, t);

и тд
и каждый раз эти изменения затрагивают 3! места:
1. конструктор класса Pos
2. метод Zakaz::NewPos
3. клиентский код, который этот NewPos вызывает.
1 и 3 место как бы неизбежны, а вот менять постоянно Zakaz::NewPos быстро надоест, учитывая что изменения сугубо механические - изменить аргументы, передать эти аргументы в конструктор класса Pos.

Автор: uranpro 12.3.2010, 11:25
а если сделать так
Код

static protected internal Pos GetNewInstance(object[] args)
public Pos NewPos(object[] args)


---
лучше даже так =)
Код

static protected internal Pos GetNewInstance(params object[] args)
public Pos NewPos(params object[] args)

Автор: 17dufa 12.3.2010, 16:03
uranpro, кстати, интересный вариант smile 
но и у него есть минус - выключается компайл-тайм проверка типов.
вообщем никак не пойму, к какому же решению меня подталкивали на собеседовании. ща прям у них и спрошу.
как вариант делать кроме класса Pos еще некоторую структуру PosInitializationData и соответственно:
Код

static protected internal Pos GetNewInstance(PosInitializationData data)
public Pos NewPos(PosInitializationData data)

и меняй эту структуру скока хошь.

Автор: uranpro 12.3.2010, 16:09
=) спроси, тож интересно стало)) да, можно и так

Автор: 17dufa 12.3.2010, 20:32
немного неожиданно, но ответили:
Код

public class Order

    {

        private IList<Item> _itemList;

        public long Id { get; protected set; }

        public string Customer {get;set;} //не выделяем в отдельную сущность (по условию задачи)

        public string Date {get; set;}

        public double Sum { get { /* реализация*/return 0; } }

 

        public Order()

        {

            _itemList = new List<Item>();

        }

 

        public virtual ReadOnlyCollection<Item> Items

        {

            get

            {

                return new ReadOnlyCollection<Item>(_itemList);

            }

        }

 

        protected internal void AddItem(Item item)

        {

            if (item != null) //в упрощенном варианте

                _itemList.Add(item);

        }

    }

 

    public class Item

    {

        public long Id { get; protected set; }

        public string Material { get; set; } //не выделяем в отдельную сущность (по условию задачи)

        public double Count { get; set; }

        public double Price { get; set; }

        public double Sum { get { /* реализация*/return 0; } }

        public Order Order{get; protected set;}

 

        public Item(Order order)

        {

            Order = order;

            order.AddItem(this);

        }

    }


Добавлено @ 20:34
то есть практически второй предложенный мной вариант, когда позиции сами добавляют себя в заказ, хех немного не дожал вопросик.

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

Автор: mihryak 13.3.2010, 01:56
вообще, решение "DTO" (3ий вариант) довольно широко применяется
т.е. общение клиент-сервис идёт посредством контейнеров с данными вместо сущностных классов в исходном виде

Автор: uranpro 13.3.2010, 11:08
=)

Автор: 17dufa 15.3.2010, 14:13
mihryak, я со счета сбился, третий - это с созданием структуры PosInitializationData?

Автор: mihryak 17.3.2010, 21:51
это я сбился smile
Цитата(17dufa @  11.3.2010,  15:31 Найти цитируемый пост)
создать еще один класс (точнее наверно структуру) с названием Данные_Позиции

про этот вариант написал

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)