Поиск

Полиморфизм.

По-моему, самое короткое и выразительное определение полиморфизма таково: это функциональная возможность, позволяющая старому коду вызвать новый. Это свойство ООП, пожалуй, наиболее ценно, поскольку дает вам возможность расширять и совершенствовать свою систему, не затрагивая существующий код.

Предположим, вам нужно написать метод, в котором для каждого объекта из набора Employee вызывается метод CakulatePay. Все просто, если зарплата рассчитывается одним способом: вы можете сразу вставить в набор тип нужного объекта. Проблемы начинаются с появлением других форм оплаты. Допустим, у вас уже есть класс Employee, реализующий расчет зарплаты по фиксированному окладу. А что делать, чтобы рассчитать зарплату контрактников — ведь это уже другой способ расчета! В случае с процедурным языком вам пришлось бы переделать функцию, включив в нее новый тип обработки, так как в прежнем коде такой обработки нет. А объектно-ориентированный язык благодаря полиморфизму позволяет делать различную обработку.

В нашем примере надо описать базовый класс Employee, а затем создать производные от него классы для всех форм оплаты (упомянутых выше). Каждый производный класс будет иметь собственную реализацию метода CakulatePay. Здесь и начинается самое интересное. Возьмите указатель на объект, приведите его к типу-предку и вызовите метод этого объекта, а средства языка времени выполнения обеспечат вам, благодаря полиморфизму, вызов той версии этого метода, которая вам требуется. Поясним сказанное на примере.

using System;
class Employee
{
public Employee(string firstName, string lastName, int age, double payRate)
{
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.payRate = payRate; }
protected string firstName;
protected string lastName;
protected int age;
protected double payRate;
public virtual double CalculatePay(int hoursWorked)
{
Console.WriteLine("Employee.CalculatePay"); return 42; // произвольное число
} >
class SalariedEmployee : Employee {
public SalariedEmployee(string firstName, string lastName,
int age, double payRate) : base(firstName, lastName, age, payRate) {}
public override double CalculatePay(int hoursWorked) {
Console.WriteLine("SalariedEmployee.CalculatePay"); return 42; // произвольное число } } .
class ContractorEmployee : Employee {
public ContractorEmployee(string firstName, string lastName, int age, double payRate)
: base(firstName, lastName, age, payRate)
<}
public override double CalculatePay(int hoursWorked) {
Console.WriteLineC'ContractorEmployee.CalculatePay");
return 42; // произвольное число } }
class HourlyEmployee : Employee {
public HourlyEmployee(string firstName, string lastName, int age, double payRate)
: base(firstName, lastName, age, payRate)
{}
public override double CalculatePay(int hoursWorked) <
Console.WriteLine("Hou rlyEmployee.CalculatePay");
return 42; // произвольное число > }
class PolyApp {
protected Employee[] employees;
protected void LoadEmployeesQ
{
Console.WriteLine("Загрузка информации о сотрудниках...");
// В реальном приложении эти сведения мы // возьмем, наверное,
из базы данных, employees = new Employee[3];
employees[0] = new SalariedEmployee ("Amy", "Ariderson", 28, 100);
employees[1] = new ContractorEmployee ("John", "Maffei", 35, 110);
employees[2] = new HourlyEmployee ("Lani", "Ota", 2000, 5);
Console. Writel_ine( "n"); }
protected void CalculatePayO .
{
foreach(Employee emp in employees)
<
emp.CalculatePay(40);
}
public static void Main()
{
PolyApp app = new PolyAppQ;
app.LoadEmployees(); app. CalculatePayO; } }
В результате компиляции и запуска этого приложения будут получены такие результаты:
c:>PolyApp
Загрузка информации о сотрудниках...
SalariedEmployee.CalculatePay ContractorEmployee.CalculatePay HourlyEmployee.CalculatePay

Полиморфизм имеет минимум два плюса. Во-первых, он позволяет группировать объекты, имеющие общий базовый класс, и последовательно (например, в цикле) их обрабатывать. В рассмотренном случае у меня три разных типа объектов (SalariedEmployee, ContractorEmployee и Hourly-Employee), но я вправе считать их все объектами Employee, поскольку они произведены от базового класса Employee. Поэтому их можно поместить в массив, описанный как массив объектов Employee. Во время выполнения вызов метода одного из этих объектов будет преобразован, благодаря полиморфизму, в вызов метода соответствующего производного объекта.

Второе достоинство я упоминал в начале этого раздела: старый код может использовать новый код. Заметьте: метод PolyApp.Calculate Pay перебирает в цикле элементы массива объектов Employee. Поскольку объекты приводятся неявно к вышестоящему типу Employee, а реализация полиморфизма во время выполнения обеспечивает вызов надлежащего метода, то ничто не мешает нам добавить в систему другие производные формы оплаты, вставить их в массив объектов Employee, и весь существующий код продолжит работу в своем первоначальном виде!

Подведем итоги

В этой главе на вас обрушился целый поток терминов и концепций ООП. Более углубленное изучение этой темы заняло бы еще не одну главу и отвлекло от основной цели этой книги. Однако только уверенное владение основами ООП поможет вам извлечь максимальную пользу из языка С#.

Мы затронули здесь несколько важных идей. Ключом к пониманию объектно-ориентированных систем является знание различий между классами, объектами и интерфейсами, а также умение применить эти концепции для получения эффективных решений. Качество объектно-ориентированных решений зависит и от разумной реализации трех принципов ООП: инкапсуляции, наследования и полиморфизма. Концепции, представленные в этой главе, закладывают фундамент для следующих глав, посвященных технологиям Microsoft .NET Framework и Common Language Runtime.