From 151d77ee54ab621d5b58e8af0f55665da197a1d0 Mon Sep 17 00:00:00 2001 From: Alexander Date: Mon, 27 Feb 2023 01:22:09 +0300 Subject: [PATCH] sources --- .../README.md | 20 +++++++ .../src/chapter-6-1/app.d | 43 +++++++++++++ .../src/chapter-6-11/app.d | 23 +++++++ .../src/chapter-6-13-1/app.d | 60 +++++++++++++++++++ .../src/chapter-6-14/app.d | 50 ++++++++++++++++ .../src/chapter-6-2/app.d | 29 +++++++++ .../src/chapter-6-3-1/app.d | 23 +++++++ .../src/chapter-6-3/app.d | 14 +++++ .../src/chapter-6-4-5/app.d | 31 ++++++++++ .../src/chapter-6-4/app.d | 39 ++++++++++++ .../src/chapter-6-8-1/app.d | 8 +++ 11 files changed, 340 insertions(+) create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-1/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-11/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-13-1/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-14/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-2/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-3-1/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-3/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-4-5/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-4/app.d create mode 100644 06-классы-объектно-ориентированный-стиль/src/chapter-6-8-1/app.d diff --git a/06-классы-объектно-ориентированный-стиль/README.md b/06-классы-объектно-ориентированный-стиль/README.md index 6b76502..e7e7952 100644 --- a/06-классы-объектно-ориентированный-стиль/README.md +++ b/06-классы-объектно-ориентированный-стиль/README.md @@ -105,6 +105,8 @@ unittest Обратите внимание на небольшую хитрость. В приведенном коде использовано выражение `w.defaultName`, а не `Widget.defaultName`. Для обращения к статическому члену класса всегда можно вместо имени класса использовать имя экземпляра класса. Это возможно, потому что при обработке выражения слева от точки сначала выполняется разрешение имени и только потом идентификация объекта (если потребуется). Выражение w в любом случае вычисляется: будет оно использовано или нет. +[Исходный код](src/chapter-6-1/) + [В начало ⮍](#6-1-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ## 6.2. Имена объектов – это ссылки @@ -214,6 +216,8 @@ if (‹условие›) Сравним ссылочную семантику с семантикой значений а-ля `int`. У семантики значений есть свои преимущества, среди которых выделяется логический вывод: в выражениях всегда можно заменять равные значения друг на друга, при этом результат не изменяется. (А к ссылкам, использующим для изменения состояния объектов вызовы методов, такой подход неприменим.) Другое важное преимущество семантики значений – скорость. Но даже если вы воспользуетесь динамической щедростью полиморфизма, от ссылочной семантики никуда не деться. Некоторые языки пытались предоставить возможность использовать и ту, и другую семантику и заслужили прозвище «нечистых» (в противоположность чисто объектно-ориентированным языкам, использующим ссылочную семантику унифицированно для всех типов). D нечист и очень гордится этим. Во время разработки необходимо принять решение: если вы желаете работать с некоторым типом в рамках объектно-ориентированной парадигмы, следует выбрать тип `class`; иначе придется использовать тип `struct` и поступиться всеми удобствами ООП, присущими ссылочной семантике. +[Исходный код](src/chapter-6-2/) + [В начало ⮍](#6-2-имена-объектов-это-ссылки) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ## 6.3. Жизненный цикл объекта @@ -239,6 +243,8 @@ unittest При вычислении выражения `new Test` конструируется объект типа `Test` с состоянием по умолчанию, то есть экземпляр класса `Test`, каждое из полей которого инициализировано своим значением по умолчанию. Любой тип `T` обладает статически известным значением по умолчанию, обратиться к которому можно через свойство `T.init` (значения свойств `.init` для базовых типов приведены в табл. 2.1). Если вы хотите инициализировать некоторые поля значениями, отличными от соответствующих значений свойства `.init`, укажите при определении этих полей статически известные инициализирующие значения, как показано в предыдущем примере для поля `a`. Выполнение теста модуля при этом не порождает исключений, так как это поле явно инициализируется константой `0.4`, а поле `b` не трогали, а значит, оно неявно инициализируется значением выражения `double.init`, то есть NaN («нечисло»). +[Исходный код](src/chapter-6-3/) + [В начало ⮍](#6-3-жизненный-цикл-объекта) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ### 6.3.1. Конструкторы @@ -303,6 +309,8 @@ class NoGo Обычные правила перегрузки функций (раздел 5.5) применимы и к конструкторам: класс может определять любое количество конструкторов, но каждый из них должен обладать уникальной сигнатурой (отличающейся числом или типом параметров, хотя бы на один параметр). +[Исходный код](src/chapter-6-3-1/) + [В начало ⮍](#6-3-1-конструкторы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ### 6.3.2. Делегирование конструкторов @@ -722,6 +730,8 @@ unittest Если бы занявший место экземпляра класса `Contact` экземпляр класса `Friend` вел себя *в точности* так же, как и экземпляр ожидаемого класса, отпали бы все (или почти все) причины использовать класс `Friend`. Одно из основных средств, предоставляемых объектной технологией, – возможность классам-наследникам переопределять функции классов-предков и таким образом модульно настраивать поведение сущностей среды. Как можно догадаться, переопределение задается с помощью ключевого слова `override` (класс `Friend` переопределяет метод `bgColor`), которое обозначает, что вызов `c.bgColor()` (где вместо c ожидается объект типа `Contact`, но на самом деле используется объект типа `Friend`) всегда инициирует вызов версии метода, предлагаемой классом `Friend`. Друзья всегда остаются друзьями, даже если компилятор думает, что это обыкновенные контакты. +[Исходный код](src/chapter-6-4/) + [В начало ⮍](#6-4-методы-и-наследование) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ### 6.4.1. Терминологический «шведский стол» @@ -902,6 +912,8 @@ void workWith(TextWidget tw) Чтобы максимизировать количество доступной статической информации о типах, D вводит средство, известное как *ковариантные возвращаемые типы*. Звучит довольно громко, но смысл ковариантности возвращаемых типов довольно прост: если родительский класс возвращает некоторый тип `C`, то переопределенной функции разрешается возвращать не только `C`, но и любого потомка `C`. Благодаря этому средству можно позволить методу `TextWidget.duplicate` возвращать `TextWidget`. Не менее важно, что теперь вы можете прибавить себе веса в дискуссии, вставив при случае фразу «ковариантные возвращаемые типы». (Шутка. Если серьезно, даже не пытайтесь.) +[Исходный код](src/chapter-6-4-5/) + [В начало ⮍](#6-4-5-ковариантные-возвращаемые-типы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ## 6.5. Инкапсуляция на уровне классов с помощью статических членов @@ -1140,6 +1152,8 @@ unittest Обратите внимание: вместе с именем класса возвращено и имя модуля, в котором класс был определен. По умолчанию модуль получает имя файла, в котором он расположен, но это умолчание можно изменить с помощью объявления с ключевым словом `module` (см. раздел 11.8). +[Исходный код](src/chapter-6-8-1/) + [В начало ⮍](#6-8-1-string-tostring) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ### 6.8.2. size_t toHash() @@ -1882,6 +1896,8 @@ class Outer } ``` +[Исходный код](src/chapter-6-11/) + [В начало ⮍](#6-11-вложенные-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ### 6.11.1. Вложенные классы в функциях @@ -2244,6 +2260,8 @@ class StorableShape : Shape } ``` +[Исходный код](src/chapter-6-13-1/) + [В начало ⮍](#6-13-1-переопределение-методов-в-сценариях-множественного-порождения-подтипов) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ## 6.14. Параметризированные классы и интерфейсы @@ -2325,6 +2343,8 @@ unittest Как только вы создадите экземпляр параметризированного класса, он превратится в обычный класс, так что `StackImpl!int` – это такой же класс, как и любой другой. Именно этот конкретный класс реализует `Stack!int`, поскольку в формочку для вырезания `StackImpl(T)` под видом `T` вставили `int` по всему телу этого класса. +[Исходный код](src/chapter-6-14/) + [В начало ⮍](#6-14-параметризированные-классы-и-интерфейсы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль) ### 6.14.1. И снова гетерогенная трансляция diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-1/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-1/app.d new file mode 100644 index 0000000..53af303 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-1/app.d @@ -0,0 +1,43 @@ +class Widget +{ + // Константа + enum fudgeFactor = 0.2; + + // Разделяемое неизменяемое значение + static immutable defaultName = "A Widget"; + + // Некоторое состояние, определенное для всех экземпляров класса Widget + string name = defaultName; + uint width, height; + + // Статический метод + static double howFudgy() + { + return fudgeFactor; + } + + // Метод + void changeName(string another) + { + name = another; + } + + // Метод, который нельзя переопределить + final void quadrupleSize() + { + width *= 2; + height *= 2; + } +} + +unittest +{ + // Обратиться к статическому методу класса Widget + assert(Widget.howFudgy() == 0.2); + // Создать экземпляр класса Widget + auto w = new Widget; + // Поиграть с объектом типа Widget + assert(w.name == w.defaultName); // Или Widget.defaultName + w.changeName("Мой виджет"); + assert(w.name == "Мой виджет"); +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-11/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-11/app.d new file mode 100644 index 0000000..0df94e8 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-11/app.d @@ -0,0 +1,23 @@ +class Outer +{ + int x; + + class Inner + { + int y; + + this() + { + x = 42; + // x – то же, что this.outer.x + assert(this.outer.x == 42); + } + } +} + +unittest +{ + auto outer = new Outer; + auto inner = outer.new Inner; + assert(outer.x == 42); // Вло­жен­ный объ­ект inner из­ме­нил внеш­ний объ­ект outer +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-13-1/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-13-1/app.d new file mode 100644 index 0000000..c4a8bf2 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-13-1/app.d @@ -0,0 +1,60 @@ +import std.stdio : writeln; + +class Shape +{ + protected string _name; + abstract void print(); +} + +class DBObject +{ + protected string _name; + abstract void print(); + void saveState() + { + writeln(_name); + } +} + +class StorableShape : Shape +{ + private class MyDBObject : DBObject + { + this(string name) + { + _name = name; + } + + override void print() + { + writeln(_name); + } + + final override void saveState() + { + writeln(this.outer._name, "; ", _name); + } + } + + private MyDBObject _store; + alias _store this; + + this(string outName, string inName) + { + _name = outName; + _store = new MyDBObject(inName); + } + + override void print() + { + writeln(_name); + } +} + +void main() +{ + auto s = new StorableShape("first", "second"); + s.print(); // StorableShape._name + s._store.print(); // StorableShape.MyDBObject._name + s.saveState(); // StorableShape._name and StorableShape.MyDBObject._name +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-14/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-14/app.d new file mode 100644 index 0000000..141fd73 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-14/app.d @@ -0,0 +1,50 @@ +import std.array; + +interface Stack(T) +{ + @property bool empty(); + @property ref T top(); + void push(T value); + void pop(); +} + +class StackImpl(T) : Stack!T +{ + private T[] _store; + + @property bool empty() + { + return _store.empty; + } + + @property ref T top() + { + assert(!empty); + return _store.back; + } + + void push(T value) + { + _store ~= value; + } + + void pop() + { + assert(!empty); + _store.popBack(); + } +} + +void main() +{ + auto stack = new StackImpl!int; + assert(stack.empty); + stack.push(3); + assert(stack.top == 3); + stack.push(5); + assert(stack.top == 5); + stack.pop(); + assert(stack.top == 3); + stack.pop(); + assert(stack.empty); +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-2/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-2/app.d new file mode 100644 index 0000000..0abcb76 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-2/app.d @@ -0,0 +1,29 @@ +import std.stdio; + +class A +{ + int x = 42; +} + +unittest +{ + { + auto a1 = new A; + assert(a1.x == 42); + auto a2 = a1; + a2.x = 100; + assert(a1.x == 100); + } + { + auto a1 = new A; + auto a2 = new A; + a1.x = 100; + a2.x = 200; + // Заставим a1 и a2 обменяться привязками + auto t = a1; + a1 = a2; + a2 = t; + assert(a1.x == 200); + assert(a2.x == 100); + } +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-3-1/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-3-1/app.d new file mode 100644 index 0000000..fcf0bac --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-3-1/app.d @@ -0,0 +1,23 @@ +import std.math; + +class Test +{ + double a = 0.4; + double b; + + this(int b) + { + this.b = b; + } + + this() {} +} + +unittest +{ + auto t = new Test(5); + auto z = new Test; + + assert(t.b == 5); + assert(isNaN(z.b)); +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-3/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-3/app.d new file mode 100644 index 0000000..aa9165e --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-3/app.d @@ -0,0 +1,14 @@ +import std.math; + +class Test +{ + double a = 0.4; + double b; +} + +unittest +{ + // Объект создается с помощью выражения new + auto t = new Test; + assert(t.a == 0.4 && isNaN(t.b)); +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-4-5/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-4-5/app.d new file mode 100644 index 0000000..78fada1 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-4-5/app.d @@ -0,0 +1,31 @@ +class Widget +{ + this(Widget source) + { + + } + + Widget duplicate() + { + return new Widget(this); // Вы­де­ля­ет па­мять и вы­зы­ва­ет this(Widget) + } +} + +class TextWidget : Widget +{ + this(TextWidget source) + { + super(source); + } + + override TextWidget duplicate() + { + return new TextWidget(this); + } +} + +void main() +{ + TextWidget tw; + TextWidget clone = tw.duplicate(); +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-4/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-4/app.d new file mode 100644 index 0000000..c983c2c --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-4/app.d @@ -0,0 +1,39 @@ +class Contact +{ + string bgColor() + { + return "Серый"; + } +} + +class Friend : Contact +{ + string currentBgColor = "Светло-зеленый"; + string currentReminder; + + this(ref string c) + { + currentBgColor = c; + } + + override string bgColor() + { + return currentBgColor; + } + + string reminder() + { + return currentReminder; + } +} + +unittest +{ + string startColor = "Синий"; + Friend f = new Friend(startColor); + Contact c = f; // Подставить экземпляр класса Friend вместо экземпляра класса Contact + auto color = c.bgColor(); // Вызвать метод класса Friend + + import std.stdio : writeln; + writeln(color); +} diff --git a/06-классы-объектно-ориентированный-стиль/src/chapter-6-8-1/app.d b/06-классы-объектно-ориентированный-стиль/src/chapter-6-8-1/app.d new file mode 100644 index 0000000..11969a6 --- /dev/null +++ b/06-классы-объектно-ориентированный-стиль/src/chapter-6-8-1/app.d @@ -0,0 +1,8 @@ +module test; + +class Widget {} + +unittest +{ + assert((new Widget).toString() == "test.Widget"); +}