Ленивые свойства объекта

    Возможно, не самый умный экземпляр для линча, но хотелось с чего-нибудь начать)

    Для красоты кода и удобства написания модулей, реализовал классы по работе с отдельными информационными сущностями. К примеру, такая вещь как язык интерфейса имеет свой ID, и для большинства операций этой информации вполне достаточно. В то же время, возникают редкие ситуации, когда требуется получить дополнительные сведения о нём (RFC-код, к примеру, узнать который можно, обратившись к отдельной таблице или файлу). Не особо задумываясь, поставил инициализацию переменных в конструктор класса… Проблемы начались, когда переехал на машину с тормозящим SQL-сервером (очень полезное занятие, как оказалось) – тестовая машина падала в таймаут при совершенно безобидных операциях. Не комильфо – процессорное время и SQL-соединения хотелось бы использовать более рационально :) Но использовать специальные методы для подгрузки вроде Fill() тоже не особо хотелось. В итоге пришел к наиболее простому (на мой взгляд) решению из доступных:

    public class Лентяй
    {
    private String коллектор;

      public String Значение
      {
        get
        {
          if (коллектор == null)
          {
            коллектор = РесурсоёмкийКодПолученияИнформации();
          }
          return коллектор;
        }
      }
    }


    Таким образом, практически не производится изменений кода (при создании свойства с именем старой переменной, и последующем переименовании переменной), и в отдельных случаях избыточная нагрузка снижается до нуля. Но сомнения насчёт рациональности всё же остались.
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 42

      +6
      настоятельно рекомендую ознакомиться с литературой по паттернам и антипатеернам. что из них полезнее сказать сложно, но польза от ознакомления будет однозначно. главное относиться к ним без фанатизма ;)
        +1
        В контексте веб-разработки самой полезной книгой будет "Разработка корпоративных приложений", Фаулера.
          0
          Вовремя я сюда заглянул, уже едва в книжный не уехал :)
          эта?
          http://www.ozon.ru/context/detail/id/161…
            0
            Она. Просто обязательна для ознакомления. Если нет в наличии GOF, рекомендую и ее приобрести.
              0
              Кстати, в ней есть и о Lazy Loading - то, что вы изобрели :)
                0
                Ну только не изобрёл, а нашел ;) И Фаулер этим термином пользуется, да и звучит не так по-пионерски)
                Книгу купил, спасибо, изучаю)
          0
          и это... может стоит исправить тег на c.sharp ? а то "#" както некорректно себя ведет в контесте хабра
            +1
            Так это легкий вариант синглтона, у меня таким способом куча-кучная вещей реализована (от редкопользуемый пропертей до хранения интерфейсов других модулей, которые тоже не пойми когда дергаются).
            pietrovich прав - GOF выдал в своё время великую книгу по паттернам (http://www.ozon.ru/context/detail/id/245…) и её хотя-бы обзорно почитать стоит. А лично мне нравится как паттерны описываются здесь (и с примерами на C#).
              +1
              Вот так всегда, в процессе реструктуризации обязательно велосипед получается))
              Пульки уже кончились, потому просто спасибо!)
                0
                Заметка всё равно имеет право на жизнь. Может кого другого спасет от вело-строительства :)
                +2
                Кстати да, тоже смотрю, вижу синглтон, и думаю, где подвох :) В рациональности же такого подхода сомневаться не приходится, до тех пор пока вы не натолкнетесь на лишнюю трату памяти из-за кэширования слишком большого, но не используемого в дальнейшем, объекта.
                  0
                  Согласен. GC от кривых рук не поможет и за памятью надо посматривать :)
                    0
                    имхо, в случае одноразового использования этого объекта его бы и создавли непосредственно перед использованем. раз уж дошло до ленивой инициализаци, то думаю в данном случае подход оправдан ;)
                    хотя замечание дельное, в дальнейшем кого-то может натолкнуть на "правильные" мысли :)
                    0
                    раз уж речь зашла про синглетоны, то в контексте "лени" мне более симпатична идея реализованнаяч в пятом варианте вот здесь: http://www.yoda.arachsys.com/csharp/sing…
                      0
                      Заметка про синглтон+поток очень полезна.
                      Правда меня в большинстве случаев устраивает первый вариант с атрибутом [ThreadStatic].
                        0
                        точно! именно этот вопрос и наводил на размышления))
                        ещё раз спасибо, посмотреть профиль pietrovich, посмотреть профиль Yustos
                      0
                      А в сишарпе таким образом объявленная переменная "коллектор" является static?
                        0
                        Нет, в данном примере нет. Для каждого объекта типа Лентяй будет свой коллектор.
                          0
                          Тогда это не singleton, а lazy load (что и отраженов названии класса). Singleton должен быть один на программу.
                            0
                            Вы правы.
                        0
                        Где вы здесь singleton увидели, подскажите пожалуйста. И что значит "легкий вариант синглтона"? :)
                          0
                          Да как где... В примере :)
                          Просто ни коллектор не статический, ни доступ к нему не статический. Вообще нихт статики (впрочем, можно поставить статик коллектору и будет уже другой эффект).
                          Вот отсутствием статики он отличается от синглтона, а проверкой на инициализанутость и создание по необходимости инстанса похож.
                          Не вникая в назначение синглтонов, но глядя в пример тут: http://www.dofactory.com/Patterns/Patter…
                          Видим:
                          ...
                              private static Singleton instance;
                          ...
                              public static Singleton Instance()
                              {
                                  // Use 'Lazy initialization' <--- :)
                                  if (instance == null)
                                  {
                                      instance = new Singleton();
                                  }
                                  return instance;
                              }

                          А "легкий", потому что реализован пропертей класса.
                        0
                        Да. Вот еще вспомнил.
                        В примере используется String, который допускает null. А ведь нужны и valueвые типы, вроде int. Его тоже можно долго вычислять. Например, проверить для объекта наличие в каких-то больших списках чего-либо.
                        Можно инициализировать каким-то значением, вроде 0, или -1, или что-то другое и надеяться, что такое значение туда кроме инициализации никогда не попадет. Но грабли есть грабли.
                        Небольшая модификация:
                            private int? _val;
                            public int val
                            {
                                get
                                {
                                    if (!_val.HasValue)
                                        _val = ДолгаяФункцияПоВычислению();
                                    return _val.Value;
                                }
                            }

                        И спим спокойно :)
                          0
                          Для корректности:
                          private int? _val = null;
                          0
                          Это частный случай Lazy Loading, судя по коду.
                            0
                            потому и вспомнились паттерны :)
                            0
                            синглтона тут я не вижу.
                            в get/set вызывать обращение к DB (правильно я понял?) не совсем корректно на мой взгляд. если это сущность, то обрабатываться она должна как одно целое. entity должно загружаться полностью и за один раз (если в этом нет необходимости, см ниже)

                            (!) кроме того, подумайте про thread safe, в данном блоке необходима синхронизация (опять же, я не уверен на 100%, так как не знаю всего кода в целом)

                            я бы порекомендовал использовать паттерн "фабрика" в случае, если таких объектов, как вы указали не очень много. если же Вам нужно подгружать "легковесные" объекты, количество которых достаточно большое, то следует использовать DTO объекты, которые являются урезанными версиями entity и загружаются из базы через DAO слой.
                              0
                              1) Да, обращение именно в DB
                              2) Да, тут необходимо было продумать потоки, именно это мне и не давало покоя)
                              3) Взял на заметку. Спасибо)
                                +1
                                - на Вашем месте я бы начал с профайлинга базы. нужно выяснить, почему получаются тормоза, какие конкретно запросы подвисают. в конце-концов это значение в любом случае придется извлечь, почему перестает тормозить, когда вы используете отложенную загрузку?
                                - метод fill() не совсем корректен. где он будет реализован и как возратит результат? в случае кластеров у вам обязательно будут будут проблемы с маршалингом. обычно загрузку объектов из DB выносят в отдельный слой, методы DAO возращают корректно сконструированные объекты DTO. DAO слой зовется из слоя бизнес логики. Business Layer возращает корректно сформированную модель. таким образом получается такой flow: DB > DAO > BUSINESS > ready-to-use model > WEB-LAYER. при такой организации очень просто реализовать кэш, если Вам это понядобится.
                                  0
                                  нужно выяснить, почему получаются тормоза, какие конкретно запросы подвисают

                                  Начиная с логина и простейших SELECT'ов. Либо слишком мало ОЗУ, либо ещё что-то, не сильно относящееся к тебе разговора. Но свою роль в дисциплинировании сыграло) Конкретно данный участок перестаёт тормозить просто потому, что практически не обращается к БД.
                                    0
                                    Либо слишком мало ОЗУ, либо ещё что-то, не сильно относящееся к тебе разговора

                                    проверьте количество подключений (кол-во выделяемых объектов Connection)
                                    Конкретно данный участок перестаёт тормозить

                                    1) значит, на самом деле нужно сделать кэш (только поищите стандартные реализации!!, нет смысла писать велосипед)
                                    2) при нагрузке все равно возможны тормоза, нужно выяснить в чем проблема в базе, чтобы быть спокойным, что это потом не всплывет
                                      0
                                      Я не удивлюсь, если в обсуждаемой системе на каждый чих делается запрос к базе.
                                      То бишь, в рамках одного запроса к странице из базы таскаются одни и те же данные в полном объёме.
                                      0
                                      + поищите незакрытые Connection
                                      0
                                      метод fill() не совсем корректен. где он будет реализован и как возратит результат?
                                      Ну, если начать просто перебирать возможные варианты, то существует как минимум два))
                                      Fill(), который производит необходимые операции, назначает публичным переменным значения и возвращает retval
                                      Fill(), который просто возвращает искомое значение и записывает его в переменную класса для дальнейшего использования.
                                      Но мне это кажется таким моветоном (особенно учитывая вишеуказанный flow :) ), что привёл его исключительно для проформы.
                                    0
                                    При чём тут синхронизация? Если обращение к свойству многопоточное, синхронизация нужна, если однопоточное, то не нужна. Эдак можно к любой отложенной инициализации привязать синхронизацию =)
                                      0
                                      1) web приложение ВСЕГДА - многопоточное
                                      2) я писал " (опять же, я не уверен на 100%, так как не знаю всего кода в целом)"
                                        0
                                        Да и пусть будет всегда многопоточное. Только толку с того? Почти все разработчики запросы обрабатывают синхронно, а это значит, что доступ к свойствам нестатических объектов однопоточный.

                                        Учитывая, что писатель заметки открыл для себя отложенную инициализацию, я уверен, что про асинхронную обработку запросов он не знает.
                                        Судя по тому, что Вы предлагаете синхронизацию, Вы про асинхронную обработку может быть слышали. Потому что имей Вы опыт, уже наверняка узнали бы стоимость синхронизации и не совали её куда ни попадя.
                                          0
                                          мдя, на личности Вы явно любите переходить. что-ж, пусть будет по Вашему. только у меня до сих пор нет уверенности в том, как работает кусок кода, который был приведен. и я не знаю, к своему горю, будет ли экземпляр этого класса использоваться локально или шариться. Вы же, безусловно, можете читать мысли на расстоянии. склоняюсь перед Вами.
                                            0
                                            Это был псевдокод. Для иллюстрации отложенной инициализации. Экземпляр этого класса никогда не будет создан.

                                            Читать мысли людей с предложениями использования паттерна фабрика для частичной загрузки данных труда не составляет.
                                            Абстрактная фабрика годится для случая, когда есть несколько разнообразных объектов с одинаковым поведением (интерфейсом). Например, есть набор данных для вывода в форме и стоит задача шаблонизировать вывод. Для этого случая можно сделать фабрику, чтобы абстрагировать получение рисовальщика.

                                            class DataPainter
                                            {
                                            public abstact void Draw(Data data);
                                            }

                                            class PainterFabric
                                            {
                                            public abstract DataPainter GetDataPainter();
                                            }

                                            class DataForm
                                            {
                                            private Data data;

                                            public void DrawData(PainterFabric painterFabric)
                                            {
                                            DataPainter dataPainter = painterFabric.GetDataPainter();
                                            dataPainter.Draw(data);
                                            }
                                            }

                                            Рисовальщики и фабрики наследуются от базовых. Обычно они создаются парно. После этого можете кормить в DrawData() любую фабрику, создающую экземпляры рисовальщиков по диагонали, ёлочкой и вообще, как заблагорассудится пользователю библиотекой.
                                            А для того, чтобы загрузить часть полей сущности, использовать фабрику нельзя. Потому что разные свойства у полной и неполной сущности это разное поведение и абстрагироваться здесь некуда, потому что вызывать можно только виртуальные методы, а неполная сущность потому и неполная, что у неё нет каких-то свойств и вызвать то, чего нет, нельзя.

                                            Есть какие-то аргументы по-поводу моего "чтения мыслей" или снова я перехожу на личности и Вы не знаете, где будет использоваться экземпляр этого класса? Мне лень было писать это всё в первый раз, поэтому я там написал только про синхронизацию.
                                    0
                                    Исходя из того, что отложенная инициализация это открытие и внутри проперти вызывается длинная операция, начинать надо не с записей в блогах, а с чтения хотя бы документации. Там чёрным по белому пишут в гайдлайнах, что длительные операции в свойствах надо декларировать в исключительных случаях, а в общем случае надо реализовывать такое через функцию.
                                      0
                                      Ну, в общем-то, благодаря этой записи в блоге я купил несколько книг и больше пока вопросов не возникает :)
                                        +1
                                        Могу посоветовать ещё одну.
                                        ASP.NET 2.0, Углубленное изучение. Дино Эспозито.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое