Перейти к основному содержимому

Лабораторная работа по теме: "Интерфейсы в C#"

Цель работы:
Научиться создавать и использовать интерфейсы в языке программирования C#. Понять принципы работы с интерфейсами, полиморфизмом, а также реализации интерфейсов различными классами.


Теоретическая часть

1. Что такое интерфейс в C#?

Интерфейс в C# — это контракт, который определяет набор методов, свойств и событий, которые должны быть реализованы классом или структурой. Интерфейсы используются для создания абстрактных типов данных, что позволяет гибко определять, какие методы и свойства должен содержать класс, не привязываясь к конкретной реализации.

Интерфейс определяет что должно быть сделано, но не как. Реализация интерфейса предоставляется классом, который его имплементирует.

Пример определения интерфейса:

public interface IShape
{
double Area(); // Метод для вычисления площади
double Perimeter(); // Метод для вычисления периметра
}

Любой класс, который реализует этот интерфейс, должен предоставить конкретную реализацию методов Area и Perimeter.

2. Реализация интерфейса

Классы, которые реализуют интерфейс, обязаны реализовать все его методы. Это выглядит так:

public class Circle : IShape
{
public double Radius { get; set; }

public Circle(double radius)
{
Radius = radius;
}

public double Area()
{
return Math.PI * Radius * Radius;
}

public double Perimeter()
{
return 2 * Math.PI * Radius;
}
}

3. Множественная реализация интерфейсов

В C# класс может реализовывать несколько интерфейсов одновременно. Это полезно, если необходимо, чтобы класс выполнял различные задачи, определенные разными интерфейсами.

Пример:

public interface IMovable
{
void Move();
}

public interface IStoppable
{
void Stop();
}

public class Car : IMovable, IStoppable
{
public void Move()
{
Console.WriteLine("Car is moving");
}

public void Stop()
{
Console.WriteLine("Car has stopped");
}
}

4. Полиморфизм через интерфейсы

Интерфейсы позволяют использовать полиморфизм. Объект класса, реализующего интерфейс, может быть обработан через переменную интерфейсного типа. Это позволяет писать код, который работает с интерфейсами, не зная точного типа объекта.

Пример:

IShape shape = new Circle(5);
Console.WriteLine("Area: " + shape.Area());
Console.WriteLine("Perimeter: " + shape.Perimeter());

5. Применение интерфейсов

Интерфейсы широко применяются для достижения гибкости и расширяемости программ, создания легко тестируемых и поддерживаемых приложений. Они часто используются при проектировании систем, где разные компоненты должны взаимодействовать друг с другом через заранее определенные контракты.


Общее задание (для всех вариантов):

  1. Определите интерфейс IProduct, который будет описывать продукт в магазине. Интерфейс должен содержать:

    • Свойство string Name для хранения названия продукта.
    • Свойство double Price для хранения цены продукта.
    • Метод double CalculateDiscount(double percent) для расчета цены с учетом скидки.
  2. Реализуйте этот интерфейс в двух классах:

    • Electronics — электронные товары.
    • Grocery — продукты питания.

В классе Electronics должен быть реализован метод расчета скидки, где скидка может применяться только к товарам с ценой больше 1000. В классе Grocery скидка может быть применена к любым товарам.

  1. В методе Main создайте массив продуктов, содержащий объекты обоих типов (Electronics и Grocery), и вычислите для каждого продукта его цену со скидкой. Выведите результаты на экран.

Индивидуальные задания

Вариант 1:

  1. Добавьте интерфейс IDurable, который будет содержать метод int WarrantyYears() для расчета срока гарантии.
  2. Реализуйте этот интерфейс в классе Electronics, где срок гарантии зависит от типа устройства (например, смартфоны — 1 год, телевизоры — 2 года).
  3. Модифицируйте общий код, чтобы для каждого объекта типа Electronics выводился срок гарантии.

Вариант 2:

  1. Добавьте интерфейс IExpirationDate, который будет содержать свойство DateTime ExpirationDate для хранения срока годности товара.
  2. Реализуйте этот интерфейс в классе Grocery, добавив расчет, который проверяет, истек ли срок годности.
  3. Модифицируйте общий код, чтобы для каждого объекта типа Grocery выводилась информация о том, истек ли срок годности продукта.

Вариант 3:

  1. Создайте интерфейс IRatable, который будет содержать метод void Rate(int rating) для оценки товара.
  2. Реализуйте этот интерфейс в обоих классах (Electronics и Grocery), добавив хранение и вывод средней оценки товаров.
  3. Модифицируйте общий код, чтобы каждый продукт можно было оценить (например, оценка от 1 до 5) и вывести среднюю оценку для всех товаров в конце программы.

Вариант 4:

  1. Добавьте интерфейс IComparable<IProduct>, чтобы объекты продуктов можно было сортировать по цене.
  2. Реализуйте метод сравнения в классах Electronics и Grocery.
  3. Модифицируйте общий код так, чтобы все продукты в массиве сортировались по возрастанию цены и выводились на экран.

Вариант 5:

  1. Создайте интерфейс IShippable, который будет содержать метод double CalculateShippingCost() для расчета стоимости доставки продукта.
  2. Реализуйте этот интерфейс для класса Electronics, где стоимость доставки зависит от веса товара (например, 10 рублей за кг).
  3. Модифицируйте общий код, чтобы для каждого объекта типа Electronics выводилась стоимость доставки на основе его веса.

Рефакторинг кода — это процесс улучшения внутренней структуры кода без изменения его внешнего поведения. Основная цель рефакторинга заключается в том, чтобы сделать код более чистым, понятным, поддерживаемым и эффективным, не затрагивая функциональность программы.

Основные цели и задачи рефакторинга:

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

  2. Уменьшение технического долга: Со временем код может "накапливать" ошибки, сложные зависимости и избыточные решения. Рефакторинг позволяет устранить эти недостатки, снизив сложность системы и облегчая её поддержку в будущем.

  3. Оптимизация производительности: Рефакторинг может включать в себя оптимизацию производительности кода, улучшение алгоритмов и уменьшение потребления ресурсов без изменения логики работы программы.

  4. Упрощение добавления новых функций: Чистый и хорошо структурированный код делает добавление новых функций и исправление багов проще, так как легче понять, как система работает.

Примеры рефакторинга:

  • Извлечение методов: Если один метод выполняет слишком много действий, его можно разделить на несколько методов, каждый из которых будет выполнять свою задачу.

    До рефакторинга:

    public void ProcessOrder()
    {
    // Валидация данных заказа
    // Подсчет стоимости заказа
    // Отправка подтверждения
    }

    После рефакторинга:

    public void ProcessOrder()
    {
    ValidateOrder();
    CalculateTotalPrice();
    SendConfirmation();
    }
  • Удаление дублирующегося кода: Если одинаковые фрагменты кода повторяются в разных местах, их можно вынести в отдельный метод или класс.

  • Переименование переменных и методов: Иногда наименования переменных, методов или классов не отражают их истинное предназначение. Правильные имена делают код более понятным.

  • Улучшение структуры классов: Это может включать использование наследования, интерфейсов или паттернов проектирования для лучшей организации классов и методов.

Когда нужно выполнять рефакторинг?

  • Перед добавлением новой функциональности: Если структура кода усложнилась, добавление новых функций может потребовать рефакторинга.
  • После исправления ошибок: Если ошибки возникают из-за сложного и плохо структурированного кода, рефакторинг поможет избежать их в будущем.
  • При наличии технического долга: Рефакторинг помогает "вернуть долг", накопившийся из-за поспешных или временных решений.

Когда не стоит выполнять рефакторинг?

  • При недостатке времени: Если задача срочная и требует немедленного решения, рефакторинг может быть отложен.
  • Без тестов: Поскольку рефакторинг может привести к непредвиденным ошибкам, его следует выполнять, только если есть тесты, которые гарантируют, что функциональность не изменилась.

Рефакторинг — это важная часть процесса разработки программного обеспечения, которая позволяет поддерживать код в хорошем состоянии и снижать риски накопления технического долга.

Требования к выполнению:

  1. Определение интерфейсов. Все интерфейсы должны быть корректно определены и реализованы в классах.
  2. Полиморфизм. Применяйте полиморфизм, используя интерфейсы для работы с объектами различных классов.
  3. Преобразование типов. Используйте интерфейсы для хранения и обработки различных объектов в одном массиве или коллекции.
  4. Обработка исключений. Обеспечьте обработку возможных ошибок (например, некорректные значения для скидки или срока годности).
  5. Тестирование. Программа должна быть протестирована на нескольких наборах данных для проверки правильности выполнения всех задач.
  6. Рефакторинг кода. Классы и интерфейсы должны быть вынесены в отдельные файлы и располагаться в папках Classes и Intefaces соответственно.

Пример кода:

public interface IProduct
{
string Name { get; }
double Price { get; }
double CalculateDiscount(double percent);
}

public class Electronics : IProduct
{
public string Name { get; private set; }
public double Price { get; private set; }

public Electronics(string name, double price)
{
Name = name;
Price = price;
}

public double CalculateDiscount(double percent)
{
if (Price > 1000)
return Price - (Price * percent / 100);
return Price;
}
}

public class Grocery : IProduct
{
public string Name { get; private set; }
public double Price { get; private set; }

public Grocery(string name, double price)
{
Name = name;
Price = price;
}

public double CalculateDiscount(double percent)
{
return Price - (Price * percent / 100);
}
}

public class Program
{
public static void Main(string[] args)
{
IProduct[] products = new IProduct[]
{
new Electronics("Smartphone", 1500),
new Grocery("Apple", 2.5),
new Electronics("TV", 800),
new Grocery("Milk", 1.5)
};

foreach (var product in products)
{
Console.WriteLine($"{product.Name} (Original Price: {product.Price:C}) - Discounted Price: {product.CalculateDiscount(10):C}");
}
}
}

Это лабораторное задание включает в себя как теоретические основы, так и практическое применение интерфейсов в C#, что позволяет глубже понять их использование и преимущества в реальных проектах.