Как стать автором
Обновить
13
0

Software Developer

Отправить сообщение
Слон понятия не имеет о среднем по слонам?

Не должен знать, с т.ч. зрения современных архитектур и шаблонов проектирования (паттернов).
Однако, при наличии соответствующего статического поля у ООП-класса — знает.
Именно поэтому я пишу, что статические поля у ООП-класса следует заводить только для описания типа данных, но никак не для определения данных, характеризующих множество объектов (средние значения, количество и прочие агрегирующие функции).
И потому статические поля — только readonly и immutable должны быть.
Отдельно взятый слон ничего не должен знать об агрегирующих данных, относящихся к «слонам».
А в вашем примере он может это сделать, просто обратившись с приватному статическому полю своего класса.
И все же, не могу удержаться, напишу еще.
Вот вы пишите
Ничего не будет, если поля потокобезопасные и приватные.

Вы молодец, но в моей практике вы входите буквально в единицы программистов, которые заботятся о «потокобезопасности и приватности».
А уж кто там будет копаться, что там использовать — AtomicInteger в java, или какой аналог этого есть в C#, вообще сложно представить.
Полагаю, все-таки, с учетом назревших задач, рано или поздно назреют и новые парадигмы программирования.
Ответил разом на другой ваш комментарий.
Объясните зачем тогда вообще нужны статические поля, если не для хранение общих для всех классов данных?

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

Статические элементы класса вообще к чистому ООП не имеют отношения.
Если их и использовать то для описания именно типа данных.
В этом случае статические поля должны быть readonly и immutable.
Например, поле (константу) Int32.MaxValue я отношу к описанию типа данных, а не к общим данным для всех экземпляров данного класса.

В соседнем посте вы привели пример, как сделать статические поля потокобезопасными — все это технические меры, в одном языке сработает, в другом нет. Вы напишите потокобезопасный код корректно — другой программист нет.
То, что вы написали для вычисления средних значений, суммы, все это можно реализовать и с помощью другого класса, статического хелпера или других конструкций (например, если есть класс «Машина», то вычислением этих величин могут заняться классы «Список машин» или «Машины»).

Поймите, статические поля не имеют отношения к концепции ООП.
А то, что вы пытаетесь с помощью них решить некие задачи, говорит только о том, что концепция ООП неполна/противоречива, и есть задачи, которые не имеют «нативного» решения с помощью ООП.
Конечно, использовать статические поля самое простое, проще, чем предложенные в отдельном посте сущности «Машина»/«Список Машин»/«Машины», которые образуют «метасущность».

Поскольку не хочется делать ни того, ни другого, поэтому я и пишу — хотелось бы новой парадигмы/платформы программирования, которая устраняла бы противоречивость ООП и соответствовала (маппилась) на теорию множеств.
Т.е., у вас несколько стоимостей множества, таких видов?:
— «идеальная стоимость», исходя из рекомендованной розничной цены;
— «прогнозируемая стоимость», исходя из оценки, какая часть товара будет продана со скидкой в конце сезона;
— «форс-мажорные стоимости», исходя из оценки, за сколько весь товар можно продать оптом (в начале, в середине и конце сезона",
— «форс-мажорная стоимость», исходя из оценки, если курс резко измениться в X, X, Z раз.

И для каждого случая вы высчитываете среднюю стоимость единицы номенклатуры, умножаете на кол-во единиц номенклатуры, суммируете полученные суммы по всем номенклатурам, и получаете стоимость множества. Так?
Кажется, я вклиниваюсь в чужую дискуссию, но хочется высказаться.
Не могу найти в дискуссии о какой предметной области идет речь, но от слов есть объекты учета как-то повеяло скучными вещами типа ERP-систем.

Штука в том, о какой стоимости вы говорите.
Предположим, вы торгуете спортивными товарами в розницу, вам в августе дистрибутор поставил 100500 единиц учета, каждой единице вы прописали у себя в 1С розничную стоимость,
и предполагаете, что сумма стоимостей единиц и есть стоимость всего множеств единиц.

Т.е., вы рассчитываете, что к концу сезона вы все это добро продадите по назначенной вами цене, и получите искомую, заранее вычисленную сумму.

Самое простое: если вы вдруг решите поменять бизнес-модель и продать все это добро оптом, то вам придется от суммы, которую вы высчитали, сделать скидку 50%, а и то и 70%, с учетом, что на дворе, к примеру, ноябрь, а не август уже.

Чуть более сложное:
продолжаете торговать в розницу, а уже в декабре нужно делать скидки, т.е. вводить поправку с той сумме, которую вы высчитали изначально.
даже если вы скажете, что скидки и тот объем товара, который пойдет со скидками, заранее прогнозируется, и цены единиц (из которых складывается общая сумма заранее разбиваются на группы (первым 100 шт. номенклатуры XXX в ERP назначена цена Y руб, а следующим 100 шт. номенклатуры XXX — Y-Z руб.),
то точно вы это все равно не спрогнозируете, тем более, что в декабре внезапно изменился курс, и вы думаете, что делать то — повышать цены, чтобы с летнему сезону закупить летние коллекции по новой цене, или наоборот, резко снижать цены, т.к. продать бы то, что уже есть, т.к. часть покупателей вдруг потеряла интерес к покупкам.

В итоге, если вы заведете у себя в ERP параметр «Стоимость множества всех единиц товара» и вычислите ее как простую арифметическую сумму стоимостей всех единиц учета, то вы, конечно, будете очень рады простому решению, но толку от такой суммы мало, а если вы ее попытатесь использовать в бизнес-прогнозах, то прогорите.
Статический класс — это вообще костыль, не имеющий отношения к ООП. Но формально класс.

Здорово, что у вас есть это понимание.
Я привел его как самый простой пример синтаксического сахара, когда нам нужно свести язык и исполняющую среду.

Если вы хотите другую терминологию, вам придется найти другую парадигму.

Об этом я и пишу — в том числе тут — да, хочу другую парадигму.

Да и так ли устоялась терминология?
Хотите сказать, что термины class, struct устоялись?
А как быть с ref class и value class в C++/CLI?

А другие тогда устоялись или нет?:
sealed, final или NotInheritable?
internal или friend?
protected или family?
Это навскидку только.
Конечно, я в курсе, было/есть ООП и без классов.
Если помните, в Pascal изначально были не классы, и даже не структуры.
Вспомните, как это называлось?
— объекты!!!
Да, и как же это звучит — экземпляр объекта! (т.е. объект это тип данных).

Зато теперь из дискуссии с вами мне понятно, что в контексте обсуждаемой статьи, нет разницы между такими «группами» ООП-типов данных, как «класс», «структура», «объект» (см. выше — объект как тип данных), и, собственно, типа данных.
Точнее, мне это и раньше было понятно, но теперь вижу, что с вами вроде не должно быть разногласий по этому вопросу?

Те, данные которые вы приводите как критические для различия классов и структур, некритичны в контексте статьи.
Тем более, что приведенное вами различие — отсутствие наследования и структур (ну надо так для реализации) можно сделать и для классов — достаточно объявить его ненаследуемым (запечатанным — sealed).

Если бы для структур не были критичны еще другие признаки (передача по значению — не по ссылке, прежде всего), то для объявление структуры достаточно было бы объявить запечатанный (sealed) класс.
Можно поднять инфу, но я почти уверен, что на уровне CLR/компилятора структура (struct) и объявляется как sealed class.

Точно так же, как статический класс есть синтаксический сахар — когда мы пишем «static class» компилятор генерирует sealed class с закрытым (private) конструктором. Если не ошибаюсь, Рихтер писал об этом в CLR via C# 2.0?

В итоге что имеем:
Одна из причин темы обсуждаемой — как раз то, что устоявшееся в ООП название «класс» для обозначения типа данных входит в противоречие с термином «класс» в теории множеств, применяемой аналитиками в онтологии.
Не находите, что в некоем воображаемом новом языке программирование было бы неплохо ввести ключевое слово type (или datatype) для обозначения типа данных, а ссылочность/значимость, запечатанность, статичность задавать всегда клювевыми словами модификаторами?:
datatype MyCar
sealed datatype Int32
static datatype MyHelper
В терминах C# — нет, не является. У них существенные отличия в поведении.
Можете ли вы назвать такие отличия, которые вывели бы структуры C# из обсуждения «Классы как тип данных — Классы как множество объектов», или наоборот, ввели бы в формате «Классы как тип данных — Структуры как тип данных — Классы как множество объектов»?

В C# достаточно строго выдерживается подход «все есть объект», поэтому с value-типами тоже можно работать как с объектами. Однако это не делает все типы данных классами.

А как тогда быть с этим?:

ValueType Class
public abstract class ValueType
Предоставляет базовый класс для типов значений.
Provides the base class for value types.

Int32 Structure
public struct Int32

Structs (C# Programming Guide)
All structs inherit directly from System.ValueType, which inherits from System.Object.

Так структуры и классы одно и то же?
Или это одно в терминах .NET CLR, но разное в C# (просто реализованы через механизм классов)?

Пусть даже разное с точки зрения «чистого» C#, но вот эти отличия структур от классов позволяют ли сказать, что структуры не классы и вывести их из данной дискуссии, или наоборот, ввести, но как новую величину?

Structs (C# Programming Guide)
Within a struct declaration, fields cannot be initialized unless they are declared as const or static.
A struct cannot declare a default constructor (a constructor without parameters) or a destructor.
Structs are copied on assignment. When a struct is assigned to a new variable, all the data is copied, and any modification to the new copy does not change the data for the original copy. This is important to remember when working with collections of value types such as Dictionary<string, myStruct>.
Structs are value types and classes are reference types.
Unlike classes, structs can be instantiated without using a new operator.
Structs can declare constructors that have parameters.
A struct cannot inherit from another struct or class, and it cannot be the base of a class. All structs inherit directly from System.ValueType, which inherits from System.Object.
A struct can implement interfaces.
A struct can be used as a nullable type and can be assigned a null value.

Или все же оставим формулировку «Классы как тип данных — Классы как множество объектов», т.к. ООП-структуры с онтологической точки зрения есть ООП-классы (а если в неком языке вырождаются до «простых типов данных», то вообще не имеют отношения к данной дискуссии")?
Мы с вами, кажется, обсуждали особенности и сравнивали разные подходы инициализации объектов — конструкторы и фабричные классы (как частный случай — фабричные методы у того же самого класса).
Я задал вопрос, почему для схожих случаев, в одном случае MS использует конструкторы, а в другом — конструкторы заменила на «фабричный метод».

Вы же можете не отвечать, это вопрос как бы в воздух, для размышления. Ответ все равно только MS знает (если там вообще осмысленные причины были).

Но мне показалось, что вы ответили в стиле «Это фабричный метод, потому это фабричный метод».
Пример хороший, однако:
1. не является все же ли MaxValue характеристикой типа данных (тем более, что технически это не поле, а константа)?
2. если же нет, то (это не характеристика типа данных, а данные общие для всех экземпляров типа данных.

Если 1, то я хотел бы языка/платформы, которые четко отделяли бы 1 от 2 — на эту тему у меня отдельная статья есть.
Если 2 (как я понял, ваш вариант), то что получается: да, для Int32.MaxValue использование статиков оправдано. А для 95% других случаев это к чему приводить, т.е. когда механизм статиков из-за неверного понимания используется, когда нельзя?
Мне все-таки кажется, что в т.ч. об этом стать.
В C# все value types являются структурами, а любая структура — потомок класса Object.
Дальше — вопрос жонглирования терминами, является структура классом.

Можно пойти и дальше:
сказать, что в C# есть простые типы данных (byte, int, long), и что это не классы и даже не структуры.
Другое дело, что в .NET эти «простым типам» соответствуют структуры (а, значит, и классы?) «Byte, Int32, Int64».

Далее следует вопрос:
а что, разве можно отделить C# от CLR и .NET?
Ведь тогда простые типы данных становятся действительно простыми типами данных.
А если и так, то в случае именно такой реализации C# это уже не будет иметь отношения к теме статьи: это процедурное программирование, а обсуждается ООП.
Уточню:

Value Types в терминах современных ОО-языков это так называемые структуры.

На самом деле это те же классы (важно: в терминах ООП, не теории множеств!), отличия там чисто технические.
Если экземпляр класса (опять же, в терминах ООП) можно передавать в качестве значения параметра метода по ссылке, т.е. передается указатель на данные, то экземпляр структуры (в терминах) ООП: передается путем копирования — т.е. данные (значения полей) копируются и передаются именно они.
Можно структуру передавать и по ссылке (если выполнить так называемую «упаковку» — boxing).

Отличие между классами и структурами чисто техническое, выбирая в качестве «базы» для создаваемого вновь типа данных — структуру или класс, мы ищем баланс между экономией памяти и производительностью обработки данных.

Более того, в современных ООП языках все структуры являются потомками класса (класса!!) Object — т.к. родителя всех классов.

И еще. Да, были в более ранних языках «простые типы данных» (но это процедурная тема, и к статье отношения не имеет), а сейчас все простые типы данных реализованы как структуры (а, значит, классы).

Итого, с точки зрения предмета данной статьи, нет никакой разницы между классами (в терминах ООП) и структурами (в терминах ООП). И то, и другое — ООП-шные классы.
Поэтому в проблеме «Класс как тип данных и Класс как множество объектов» не стоит зацикливаться на технических деталях.
Иначе можно погрязнуть в технических ООП-нных чисто технических терминах — классы, структуры/value types, свойства, атрибуты, перечисления, флаги, имя им легион.
Это представление не ложное, а истинное. Статические поля действительно можно использовать как данные, общие для конкретного множества объектов (всех экземпляров этого типа в рамках application domain, если мы говорим о .net).

Предмет данной статьи как раз в том, что утверждается (мною, сама статья выше технического уровня) представление это именно ложное.
Когда вы пишите «Статические поля действительно можно использовать как данные», под словом «можно» что имеется в виду?
Что ничто не препятствует их использованию (т.е., компилятор не запрещает, а он действительно не запрещает), или с вашей точки зрения «можно» — что с точки зрения концепции можно (а, значит, и нужно) использовать эти поля как общие между всеми экземплярами типа данных (всеми элементами множества).

Поясню на примере:
в Delphi language есть секцию private, которая на самом деле никакая не private, а internal, т.к. позволяет внутри одного модуля расшаривать поля/методы/свойства класса между разными классами.

Очевидно, это «косяк» архитектуры языка, т.к. если поле помечено private, то компилятор не должен позволить обратиться к этому полю из другого класса.
Особенно, учитывая, что именно в Delphi было особенно модно внутри одного модуля писать тысячи классов.
С точки зрения ООП, есть понятие класс, и неважно где он лежит — в том же модуле/файле и т.д. Private, значит Private.

И тогда горе-программисты мне говорят — ну так и не обращайся. Да вот только суть ООП в том, чтобы здесь компилятор контролировал программиста. Программист однажды написал private (думать надо было), и все.
А иначе чем это отличается от процедурного программирования.

Появление в новых версиях секции strict private ситуации не исправляет — никто не мешает использовать обычный private, да у старый код никуда не делся.

Так и в нашем случае — что вы имеете в виду под словом «можно»? Компилятор допускает это, или «можно» вы понимаете как «нужно» (по крайней мере, в для определенных случаев)?
Хорошие примеры подумать:

— Упомянутый вами конструктор FileStream;

— И классы XmlTextReader/XmlTextWriter, которые тоже имеют свои открытые конструкторы.
Но начиная с .NET Framework 2.0, MS создала замену: абстрактные классы XmlReader/XmlWriter, возвращающие свои экземпляры (на самом деле, понятно, реализация другая) через статические методы Create.
Почему так, и почему для тех же FileStream, StreamReader, StreamWriter по-прежнему используются конструкторы.
Есть языки, в которых исключение, порожденное в конструкторе, приведет к тому, что объект не будет создан вообще (например, C#).

Вы усложняете. ArgumentException в конструкторе — прекрасная вещь. Чтобы далеко не ходить: конструктор FileStream может кинуть девять разных искключений.

Понятно, но в общем случае (берясь за какой-то язык или его версию) нужно смотреть спецификацию — такое поведение это детали реализации конкретной версии компилятора, или же нам спецификация гарантирует это.
Т.е., экземплярный конструктор, будучи реализованным как статик-метод, что вначале делает?:
— выполняет прописанный нами код (проверки значений параметров и последующими генерацией исключений или присвоением значений полей);
— или вначале создает экземпляр, а, затем выполняет написанный нами код (проверки/исключения/присвоения значений полей).

Ээээ, а в чем проблема? Прелесть статического метода (по крайней мере, в C#) в том, что можно создать объект, а потом обращаться к любым его полям, потому что область видимости — внутренняя.

Да, упустил это момент из виду. Хотя можно и рассмотреть, как это влияет на качество кода и его сопровождаемость.
Вопрос поиска середины «возможности — строгость».

А вот обратная возможность — как вам?:
возможность из экземплярных методов получать доступ к статик-полям класса (типа данных)? А ведь, в т.ч, именно это провоцирует программистов на ложное представление, что, заведя статик-поля, можно использовать их не просто как часть типа данных, а как данные, общие для множества объектов — всех экземпляров данного класса (экземпляров типа данных).
Начале мы с вами начали немного отклоняться от темы статьи — а вот этот момент как раз по теме.
И еще насчет фабрик.
Так ли необходимо, чтобы они имели доступ ко внутреннему состоянию объекта?
Пусть фабрика находится в одной сборке с классом, объекты которого мы создаем через фабрику.
Но почему бы нам поля — состояние объекта не сделать все-таки закрытыми (private, а не internal), и пусть фабрика инициализирует объекты путем вызова конструктора и передачи в него параметров (а еще лучше через статический метод Create — паттерн, описанный выше)?

Тем более, что если одно из назначений фабрики — скрытие деталей реализации объекта (т.е. возвращение экземпляра интерфейса или абстрактного класса), то не факт, что класс-реализация будет находиться именно в одной сборке с фабрикой. Это может быть и вполне самостоятельный класс, доступный для использования, а значит, и поля у него все будут private.
1. Фабрики да, однако в данном случае их статические методы фактически играют роль конструкторов.
Более того, класс, создание экземпляров которого предусмотрено через фабрику, без фабрики, видимо, лучше вообще не употреблять и его конструктор сделать internal, и положить класс в одну сборку с фабрикой. Тогда создание только через статик-метод фабрики.

Более того, я являюсь сторонником известной концепции «конструктор не должен порождать исключений» (по причине того, чтобы не получить экземпляр класса — объект с несогласованным внутренним состоянием, т.к. уж очень многие любят подавлять исключения).
Мы не можем полагаться на реализацию «экземплярный конструктор реализован через статический метод». К экземплярным полям он имеет доступ как-то? Значит есть вероятность получения объекта в несогласованном состоянии при подавлении исключения.

А тогда, если конструктор получает на вход параметры, значения (сочетания значений) которых могут оказаться недопустимыми,
остается только идти путь:
— экземплярный конструктор класса делаем вообще закрытым (private), который получает на вход значения и без проверок значений инициализирует поля (предполагаются, что со значениями OK);
— объявляем у класса открытый (public) статик-метод, который принимает на вход параметры, проверяет их значений, если NOT OK — генерирует исключение вида ArgumentException, если OK — вызывает экземплярный конструктор, передает ему проверенные значения входных параметров, и, собственно, возвращает вновь созданный экземпляр.

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

Однако, тут я не вижу с вами каких-то противоречений пока.
Это все как раз примеры того, что статики — это именно необходимый и опасный инструмент, применять его нужно к месту.
Сами средства и возможные Причины их применения — специализированные и технические.
Настаиваю на том, что их нельзя применять «концептуально» — например, для реализации теории множеств.
И ведь пишут в книжках и мануалах, что тот же статический конструктор класса инициализирует _тип данных_ (читай, не используйте его, например, для подгрузки данных из БД — инфы, характерной больше для множества уже созданных объектов, чем для самого типа).
А потом удивляются, почему в случае ошибки чтения из БД (да еще в многопотоке) получаем ошибку «type initialization error» и программа вообще не может двигаться дальше.
Правильно, у нас же ошибка связана не с, например, наполнением множества объектов, а с инициализацией типа данных, т.е. мы не имеем описания типа данных => не можем создать ни одного экземпляра (объекта). И именно поэтому среда исполнения не дает повторной возможности (попытки) проинициализировать тип — очевидно, что инициализация типа данных это вещь детерминированная, поэтому возможен только перезапуск программы (подавлять исключение бесполезно, как это любят делать неквалифицированные и неответственные программисты, => все равно не одного экземпляра не создадим, все равно перезапускать программу).

Информация

В рейтинге
Не участвует
Откуда
Россия
Зарегистрирован
Активность