Alexander 2 years ago
parent
commit
151d77ee54

+ 20 - 0
06-классы-объектно-ориентированный-стиль/README.md

@@ -105,6 +105,8 @@ unittest
 
 
 Обратите внимание на небольшую хитрость. В приведенном коде использовано выражение `w.defaultName`, а не `Widget.defaultName`. Для обращения к статическому члену класса всегда можно вместо имени класса использовать имя экземпляра класса. Это возможно, потому что при обработке выражения слева от точки сначала выполняется разрешение имени и только потом идентификация объекта (если потребуется). Выражение w в любом случае вычисляется: будет оно использовано или нет.
 Обратите внимание на небольшую хитрость. В приведенном коде использовано выражение `w.defaultName`, а не `Widget.defaultName`. Для обращения к статическому члену класса всегда можно вместо имени класса использовать имя экземпляра класса. Это возможно, потому что при обработке выражения слева от точки сначала выполняется разрешение имени и только потом идентификация объекта (если потребуется). Выражение w в любом случае вычисляется: будет оно использовано или нет.
 
 
+[Исходный код](src/chapter-6-1/)
+
 [В начало ⮍](#6-1-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-1-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ## 6.2. Имена объектов – это ссылки
 ## 6.2. Имена объектов – это ссылки
@@ -214,6 +216,8 @@ if (‹условие›)
 
 
 Сравним ссылочную семантику с семантикой значений а-ля `int`. У семантики значений есть свои преимущества, среди которых выделяется логический вывод: в выражениях всегда можно заменять равные значения друг на друга, при этом результат не изменяется. (А к ссылкам, использующим для изменения состояния объектов вызовы методов, такой подход неприменим.) Другое важное преимущество семантики значений – скорость. Но даже если вы воспользуетесь динамической щедростью полиморфизма, от ссылочной семантики никуда не деться. Некоторые языки пытались предоставить возможность использовать и ту, и другую семантику и заслужили прозвище «нечистых» (в противоположность чисто объектно-ориентированным языкам, использующим ссылочную семантику унифицированно для всех типов). D нечист и очень гордится этим. Во время разработки необходимо принять решение: если вы желаете работать с некоторым типом в рамках объектно-ориентированной парадигмы, следует выбрать тип `class`; иначе придется использовать тип `struct` и поступиться всеми удобствами ООП, присущими ссылочной семантике.
 Сравним ссылочную семантику с семантикой значений а-ля `int`. У семантики значений есть свои преимущества, среди которых выделяется логический вывод: в выражениях всегда можно заменять равные значения друг на друга, при этом результат не изменяется. (А к ссылкам, использующим для изменения состояния объектов вызовы методов, такой подход неприменим.) Другое важное преимущество семантики значений – скорость. Но даже если вы воспользуетесь динамической щедростью полиморфизма, от ссылочной семантики никуда не деться. Некоторые языки пытались предоставить возможность использовать и ту, и другую семантику и заслужили прозвище «нечистых» (в противоположность чисто объектно-ориентированным языкам, использующим ссылочную семантику унифицированно для всех типов). D нечист и очень гордится этим. Во время разработки необходимо принять решение: если вы желаете работать с некоторым типом в рамках объектно-ориентированной парадигмы, следует выбрать тип `class`; иначе придется использовать тип `struct` и поступиться всеми удобствами ООП, присущими ссылочной семантике.
 
 
+[Исходный код](src/chapter-6-2/)
+
 [В начало ⮍](#6-2-имена-объектов-это-ссылки) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-2-имена-объектов-это-ссылки) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ## 6.3. Жизненный цикл объекта
 ## 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 («нечисло»).
 При вычислении выражения `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-жизненный-цикл-объекта) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ### 6.3.1. Конструкторы
 ### 6.3.1. Конструкторы
@@ -303,6 +309,8 @@ class NoGo
 
 
 Обычные правила перегрузки функций (раздел 5.5) применимы и к конструкторам: класс может определять любое количество конструкторов, но каждый из них должен обладать уникальной сигнатурой (отличающейся числом или типом параметров, хотя бы на один параметр).
 Обычные правила перегрузки функций (раздел 5.5) применимы и к конструкторам: класс может определять любое количество конструкторов, но каждый из них должен обладать уникальной сигнатурой (отличающейся числом или типом параметров, хотя бы на один параметр).
 
 
+[Исходный код](src/chapter-6-3-1/)
+
 [В начало ⮍](#6-3-1-конструкторы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-3-1-конструкторы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ### 6.3.2. Делегирование конструкторов
 ### 6.3.2. Делегирование конструкторов
@@ -722,6 +730,8 @@ unittest
 
 
 Если бы занявший место экземпляра класса `Contact` экземпляр класса `Friend` вел себя *в точности* так же, как и экземпляр ожидаемого класса, отпали бы все (или почти все) причины использовать класс `Friend`. Одно из основных средств, предоставляемых объектной технологией, – возможность классам-наследникам переопределять функции классов-предков и таким образом модульно настраивать поведение сущностей среды. Как можно догадаться, переопределение задается с помощью ключевого слова `override` (класс `Friend` переопределяет метод `bgColor`), которое обозначает, что вызов `c.bgColor()` (где вместо c ожидается объект типа `Contact`, но на самом деле используется объект типа `Friend`) всегда инициирует вызов версии метода, предлагаемой классом `Friend`. Друзья всегда остаются друзьями, даже если компилятор думает, что это обыкновенные контакты.
 Если бы занявший место экземпляра класса `Contact` экземпляр класса `Friend` вел себя *в точности* так же, как и экземпляр ожидаемого класса, отпали бы все (или почти все) причины использовать класс `Friend`. Одно из основных средств, предоставляемых объектной технологией, – возможность классам-наследникам переопределять функции классов-предков и таким образом модульно настраивать поведение сущностей среды. Как можно догадаться, переопределение задается с помощью ключевого слова `override` (класс `Friend` переопределяет метод `bgColor`), которое обозначает, что вызов `c.bgColor()` (где вместо c ожидается объект типа `Contact`, но на самом деле используется объект типа `Friend`) всегда инициирует вызов версии метода, предлагаемой классом `Friend`. Друзья всегда остаются друзьями, даже если компилятор думает, что это обыкновенные контакты.
 
 
+[Исходный код](src/chapter-6-4/)
+
 [В начало ⮍](#6-4-методы-и-наследование) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-4-методы-и-наследование) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ### 6.4.1. Терминологический «шведский стол»
 ### 6.4.1. Терминологический «шведский стол»
@@ -902,6 +912,8 @@ void workWith(TextWidget tw)
 
 
 Чтобы максимизировать количество доступной статической информации о типах, D вводит средство, известное как *ковариантные возвращаемые типы*. Звучит довольно громко, но смысл ковариантности возвращаемых типов довольно прост: если родительский класс возвращает некоторый тип `C`, то переопределенной функции разрешается возвращать не только `C`, но и любого потомка `C`. Благодаря этому средству можно позволить методу `TextWidget.duplicate` возвращать `TextWidget`. Не менее важно, что теперь вы можете прибавить себе веса в дискуссии, вставив при случае фразу «ковариантные возвращаемые типы». (Шутка. Если серьезно, даже не пытайтесь.)
 Чтобы максимизировать количество доступной статической информации о типах, D вводит средство, известное как *ковариантные возвращаемые типы*. Звучит довольно громко, но смысл ковариантности возвращаемых типов довольно прост: если родительский класс возвращает некоторый тип `C`, то переопределенной функции разрешается возвращать не только `C`, но и любого потомка `C`. Благодаря этому средству можно позволить методу `TextWidget.duplicate` возвращать `TextWidget`. Не менее важно, что теперь вы можете прибавить себе веса в дискуссии, вставив при случае фразу «ковариантные возвращаемые типы». (Шутка. Если серьезно, даже не пытайтесь.)
 
 
+[Исходный код](src/chapter-6-4-5/)
+
 [В начало ⮍](#6-4-5-ковариантные-возвращаемые-типы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-4-5-ковариантные-возвращаемые-типы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ## 6.5. Инкапсуляция на уровне классов с помощью статических членов
 ## 6.5. Инкапсуляция на уровне классов с помощью статических членов
@@ -1140,6 +1152,8 @@ unittest
 
 
 Обратите внимание: вместе с именем класса возвращено и имя модуля, в котором класс был определен. По умолчанию модуль получает имя файла, в котором он расположен, но это умолчание можно изменить с помощью объявления с ключевым словом `module` (см. раздел 11.8).
 Обратите внимание: вместе с именем класса возвращено и имя модуля, в котором класс был определен. По умолчанию модуль получает имя файла, в котором он расположен, но это умолчание можно изменить с помощью объявления с ключевым словом `module` (см. раздел 11.8).
 
 
+[Исходный код](src/chapter-6-8-1/)
+
 [В начало ⮍](#6-8-1-string-tostring) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-8-1-string-tostring) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ### 6.8.2. size_t toHash()
 ### 6.8.2. size_t toHash()
@@ -1882,6 +1896,8 @@ class Outer
 }
 }
 ```
 ```
 
 
+[Исходный код](src/chapter-6-11/)
+
 [В начало ⮍](#6-11-вложенные-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-11-вложенные-классы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ### 6.11.1. Вложенные классы в функциях
 ### 6.11.1. Вложенные классы в функциях
@@ -2244,6 +2260,8 @@ class StorableShape : Shape
 }
 }
 ```
 ```
 
 
+[Исходный код](src/chapter-6-13-1/)
+
 [В начало ⮍](#6-13-1-переопределение-методов-в-сценариях-множественного-порождения-подтипов) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-13-1-переопределение-методов-в-сценариях-множественного-порождения-подтипов) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ## 6.14. Параметризированные классы и интерфейсы
 ## 6.14. Параметризированные классы и интерфейсы
@@ -2325,6 +2343,8 @@ unittest
 
 
 Как только вы создадите экземпляр параметризированного класса, он превратится в обычный класс, так что `StackImpl!int` – это такой же класс, как и любой другой. Именно этот конкретный класс реализует `Stack!int`, поскольку в формочку для вырезания `StackImpl(T)` под видом `T` вставили `int` по всему телу этого класса.
 Как только вы создадите экземпляр параметризированного класса, он превратится в обычный класс, так что `StackImpl!int` – это такой же класс, как и любой другой. Именно этот конкретный класс реализует `Stack!int`, поскольку в формочку для вырезания `StackImpl(T)` под видом `T` вставили `int` по всему телу этого класса.
 
 
+[Исходный код](src/chapter-6-14/)
+
 [В начало ⮍](#6-14-параметризированные-классы-и-интерфейсы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 [В начало ⮍](#6-14-параметризированные-классы-и-интерфейсы) [Наверх ⮍](#6-классы-объектно-ориентированный-стиль)
 
 
 ### 6.14.1. И снова гетерогенная трансляция
 ### 6.14.1. И снова гетерогенная трансляция

+ 43 - 0
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 == "Мой виджет");
+}

+ 23 - 0
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
+}

+ 60 - 0
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
+}

+ 50 - 0
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);
+}

+ 29 - 0
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);
+    }    
+}

+ 23 - 0
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));
+}

+ 14 - 0
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));
+}

+ 31 - 0
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();
+}

+ 39 - 0
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);    
+}

+ 8 - 0
06-классы-объектно-ориентированный-стиль/src/chapter-6-8-1/app.d

@@ -0,0 +1,8 @@
+module test;
+
+class Widget {}
+
+unittest
+{
+    assert((new Widget).toString() == "test.Widget");
+}