Вы уже можете!
Public Function Create(ByVal Value As Series) As IChartSeries
With New ChartSeries <~ With block variable has access to members of the ChartSeries class
.Series = Value
Set Create = .Self
End With
End Function
... только, как свойства .Series
и .Self
, он должен быть Public
членом ChartSeries
interface / class (линия размыта в VBA, поскольку каждый класс имеет интерфейс по умолчанию / также является интерфейсом).
Idiomati c Назначение объекта
Примечание об этом свойстве :
Friend Property Let Series(ByVal Value As Series)
Set This.Series = Value
End Property
Использование члена Property Let
для Set
ссылки на объект будет работать - но это уже не идиоматический c код VBA, как вы можете видеть в .Create
функция:
.Series = Value
Если мы читаем эту строку, не зная о характере свойства, это похоже на любое другое присвоение значения. Единственная проблема в том, что мы не присваиваем значение , а reference - и ссылки в VBA обычно выполняются с использованием ключевого слова Set
. Если мы изменим Let
для Set
в определении свойства Series
, нам нужно будет сделать это:
Set .Series = Value
И это будет выглядеть гораздо проще, чем референсное присвоение! Без этого, по-видимому, происходит неявное принудительное приведение в исполнение, и это делает его неоднозначным кодом: VBA требует ключевое слово Set
для ссылочных назначений, потому что любой данный объект может иметь свойство по умолчанию без параметров (например, как foo = Range("A1")
неявно присваивает foo
Value
из Range
).
Кэширование и обязанности
Теперь вернемся к методу Run
- если это Сделано Public
в классе ChartSeries
, но не доступно в реализованном интерфейсе IChartSeries
, тогда это член, который может быть вызван только из 1) экземпляра ChartSeries
по умолчанию или 2) любой объектной переменной, которая имеет ChartSeries
заявленный тип. А поскольку наш «клиентский код» работает на IChartSeries
, мы можем защититься от 1 и отмахнуться от 2.
Обратите внимание, что ключевое слово Call
является излишним, а метод Run
на самом деле просто вытягивает метаданные из инкапсулированного объекта Series
и кэширование его на уровне экземпляра - я бы дал ему имя, которое больше похоже на «refre sh кэшированные свойства», чем «запустить что-то».
Ваша догадка хороший вариант: Property Get
должна быть простой функцией возврата без каких-либо побочных эффектов. Вызов метода, который сканирует объект и сбрасывает состояние экземпляра в методе доступа Property Get
, приводит к побочным эффектам, что теоретически является запахом дизайна.
Если Run
вызывается сразу после создания до * Функция 1069 * возвращает экземпляр, затем этот метод Run
сводится к «анализу ряда и кэшированию некоторых метаданных, которые я буду использовать позже», и в этом нет ничего плохого: вызовите его из Create
и удалите из Property Get
средства доступа.
Результатом является объект, состояние которого доступно только для чтения и более надежно определено; в противоположность этому у вас теперь есть объект, состояние которого может быть не синхронизировано c с фактическим объектом Excel Series
на листе: если код (или пользователь) изменяет объект Series
после IChartSeries
инициализируется, объект и его состояние устарели.
Одним из решений является go, чтобы вы могли определить, когда серия устарела, и убедиться, что кэш обновлен.
Другим решением было бы полное устранение проблемы, больше не кэшируя состояние - это означало бы одно из двух:
Генерация графа объекта один раз при создании, эффективно Перенос ответственности за кэширование на вызывающего: вызывающий код получает «снимок» только для чтения для работы.
Создание нового графа объектов из метаданных серии каждый раз, когда вызывающий код это нужно: эффективно переносит ответственность за кэширование на вызывающую сторону, что вовсе не плохая идея.
Создание вещей только для чтения удаляет многое сложности! Я бы go с первым вариантом.
В целом, код выглядит хорошим и чистым (хотя неясно, сколько было очищено для этого поста), и вы, кажется, поняли шаблон фабричного метода , использующий экземпляр по умолчанию и предоставляющий интерфейс фасада - kudos ! Именование в целом довольно хорошее (хотя «Выполнить» торчит IMO), и объекты выглядят так, как будто они имеют четкое, определенное назначение. Хорошая работа!
Модульное тестирование
В настоящее время я полагаюсь на реальные диаграммы / объекты ChartObjects. Как создать заглушку / макет / подделка для серии? (Извините, я не знаю правильный термин.)
В настоящее время вы не можете. Когда если / когда этот PR объединен , вы сможете макетировать интерфейсы Excel (и многое, многое другое) и писать тесты для ваших классов, которые вводят макет Excel.Series
объект, который вы можете настроить для целей своих тестов ... но до тех пор именно здесь находится стена.
В то же время, лучшее, что вы можете сделать, это обернуть его собственным интерфейсом, и заглушка это. Другими словами, там, где есть шов между вашим кодом и объектной моделью Excel, мы проскальзываем интерфейс между ними: вместо того, чтобы брать объект Excel.Series
, вы бы взяли какой-то ISeriesWrapper
, а затем реальный код будет использовать ExcelSeriesWrapper
, который работает на Excel.Series
, а тестовый код может использовать StubSeriesWrapper
, свойства которого возвращают либо жестко запрограммированные значения, либо значения, настроенные тестами: код, который работает в стыке между Excel
библиотека и ваш проект не могут быть протестированы - и мы в любом случае не хотим, потому что тогда мы будем тестировать Excel, а не наш собственный код.
Вы можете увидеть это в действии в пример кода для следующей статьи RD News здесь ; эта статья будет обсуждать именно это, используя соединения ADODB. Принцип тот же: ни один из 94 модульных тестов в этом проекте никогда не открывал ни одного фактического соединения, но, тем не менее, с помощью внедрения зависимостей и интерфейсов-оболочек мы можем протестировать каждый бит функциональности, от открытия соединения с базой данных до совершения транзакции. ... не попав в реальную базу данных.