Третьим принципом ООП является
Чтобы проиллюстрировать понятие полиморфизма, снова используем иерархию форм. Предположим, что класс Shape определил метод Draw(), не имеющий параметров и не возвращающий ничего. С учетом того, что визуализация для каждой формы оказывается уникальной, подклассы (такие как Hexagon и Circle) могут переопределить соответствующий метод так, как это требуется для них (рис. 4.4).
Рис. 4.4. Классический полиморфизм
После создания полиморфного интерфейса можно использовать различные предположения, касающиеся программного кода. Например, если Hexagon и Circle являются производными от одного общего родителя (Shape)
static void Main(string [] args) {
// Создание массива элементов, производных от Shape.
Shape[] myShapes = new Shape[3];
myShapes[0] = new Hexagon();
myShapes[1] = new Circle();
myShapes[2] = new Hexagon();
// Движение по массиву и отображение элементов.
foreach (Shape s in myShapes) s.Draw();
Console.ReadLine();
}
На этом наш краткий (и упрощенный) обзор принципов ООП завершается. Теперь, имея в запасе теорию, мы исследуем некоторые подробности и точный синтаксис C#, с помощью которого реализуются каждый из указанных принципов.
Первый принцип: сервис инкапсуляции C#
Понятие инкапсуляции отражает общее правило, согласно которому поля данных объекта не должны быть непосредственно доступны из открытого интерфейса. Если пользователь объекта желает изменить состояние объекта, то он должен делать это косвенно, с помощью методов чтения (get) и модификации (set). В C# инкапсуляция "навязывается" на уровне синтаксиса с помощью ключевых слов public, private, protected и protected internal, как было показано в главе 3. Чтобы проиллюстрировать необходимость инкапсуляции, предположим, что у нас есть следующее определение класса.
// Класс с одним общедоступным полем.
public class Book {
public int numberOfPages;
}
Проблема общедоступных полей данных заключается в том, что такие элементы не имеют никакой возможности "понять", является ли их текущее значение действительным с точки зрения "бизнес-правил" данной системы. Вы знаете, что верхняя граница диапазона допустимых значений для int в C# очень велика (она равна 2147483647). Поэтому компилятор не запрещает следующий вариант присваивания.
// М-м-м-да…
static void Main(stting[] args) {
Book miniNovel = new Book();
miniNovel.numberOfPages = 30000000;
}
Здесь нет перехода за границы допустимости для данных целочисленного типа, но должно быть ясно, что miniNovel ("мини-роман") со значением 30000000 для numberOfPages (число страниц) является просто невероятным с практической точки зрения. Как видите, открытые поля не обеспечивают проверку адекватности данных. Если система предполагает правило, по которому мини-роман должен содержать от 1 до 200 страниц, будет трудно реализовать это правило программными средствами. В этой связи открытые поля обычно не находят места на уровне определений классов, применяемых для решения реальных задач (исключением являются открытые поля, доступные только для чтения).