Виділення приватного класу даних

Виділення приватного класу даних (Private class data) - це структурний шаблон проєктування, який використовується для інкапсуляції атрибутів і маніпуляції нам ними. Частковий випадок рефакторингу - "Extract Class".

Стандартна документація

ред.

Категорії документації для шаблону проєктування private class data описані згідно з матеріалами книги Gang of Four.

Ім'я та класифікація

ред.
Назва шаблону
Цей шаблон проєктування відомий як private class data.
Класифікація шаблону
Це один зі структурних шаблонів проєктування.

Мета

ред.

Шаблон проєктування private class data прагне до скорочення стороннього впливу на атрибути, обмежуючи їх область видимості. Він скорочує кількість атрибутів класу інкапсулюючи їх в Data об'єкт. Це дозволяє позбутись прав на зміну атрибутів, які за задумом, мають бути незмінними після того, як вони були ініціалізовані в конструкторі. Атрибути не можуть бути змінені навіть методами цільового класу.

Також відомий як

ред.

Pimpl (Private IMPLementation) чи непрозорий вказівник

Мотивація

ред.

Клас може змінювати значення атрибутів, навіть якщо такі зміни є небажані. Наприклад: після ініціалізації атрибутів в конструкторі класу. Використання шаблону проєктування private class data запобігає таким небажаним змінам.

Клас може мати одноразово-змінювані які не можуть бути оголошені з ключовим словом final. Використання цього шаблону дозволяє реалізувати одноразове надання значень цим атрибутам.

Ідея створення такого шаблону походить від тези про захист стану об'єкта класу шляхом мінімізації області видимості його атрибутів (даних).

Застосовність

ред.

Цей шаблон проєктування є застосовний для будь-якого класу в будь-якій об'єктно-орієнтованій мові програмування.

Структура

ред.

Учасники

ред.

Співпраця

ред.

Наслідки

ред.

Наслідки використання цього шаблону проєктування включають в себе наступне:

  • Контроль прав на запис для атрибутів класу;
  • Відділення даних від методів які використовують ці дані;
  • Інкапсуляція ініціалізації атрибутів (даних) класу;
  • Введення нового типу для службового слова final: незмінність після конструктора;

Реалізація

ред.

Шаблон проєктування private class data вирішує проблеми описані вище. Наслідками його застосування є:

  • Клас даних доступається до кожного атрибута (змінної чи властивості) через getter [Архівовано 17 березня 2015 у Wayback Machine.].
  • Клас даних змінює кожний атрибут (змінну чи властивість) після виходу з конструктора - лише через setter [Архівовано 17 березня 2015 у Wayback Machine.].

Зразки коду

ред.

Код на C#, наведений нижче, ілюструє те, яким чином можна застосувати цей шаблон:

public class Circle
{
    private double radius;
    private Color color;
    private Point origin;
    public Circle(double radius, Color color, Point origin)
    {
        this.radius = radius;
        this.color = color;
        this.origin = origin;
    }
    public double Circumference
    {
        get { return 2 * Math.PI * this.radius; }
    }
    public double Diameter
    {
        get { return 2 * this.radius; }
    }
    public void Draw(Graphics graphics)
    {
        //...
    }
}

Атрибути radius, color і origin, наведені вище не повинні змінюватись в конструкторі Circle(). Зверніть увагу що їх область видимості обмежена, оскільки вони оголошені як private, але методи класу Circle все ще можуть модифікувати їх. Надмірний вплив атрибутів створює (небажаний) зв'язок між методами які доступаються до атрибутів. Щоб зменшити область видимості атрибутів і, таким чином, зменшити зв'язність коду, ми застосуємо шаблон private class data, як показано нижче:

public class CircleData
{
    private double radius;
    private Color color;
    private Point origin;
    public CircleData(double radius, Color color, Point origin)
    {
        this.radius = radius;
        this.color = color;
        this.origin = origin;
    }
    public double Radius
    {
        get { return this.radius; }
    }
    public Color Color
    {
        get { return this.color; }
    }
    public Point Origin
    {
        get { return this.origin; }
    }
}
public class Circle
{
    private CircleData circleData;
    public Circle(double radius, Color color, Point origin)
    {
        this.circleData = new CircleData(radius, color, origin);
    }
    public double Circumference
    {
        get { return 2 * this.circleData.Radius * Math.PI; }
    }
    public double Diameter
    {
        get { return this.circleData.Radius * 2; }
    }
    public void Draw(Graphics graphics)
    {
        //...
    }
}

Клас Circle тепер має атрибут типу CircleData який інкапсулює атрибути, які раніше були доступні напряму з класу Circle. Це застерігає методи класу від зміни атрибутів після того, як був викликаний конструктор класу Circle(). Зауважте, що не зважаючи на це, методи класу Circle все ще можуть повертати значення інкапсульованих атрибутів.

Код на php.

До застосування шаблону:

class Circle 
{
    private $radius;
    private $color;
    private $origin;
    public function __construct($radius, $color, $origin) 
    {
        $this->radius = $radius;
        $this->color = $color;
        $this->origin = $origin;
    }
    public function getCircumference() 
    {
        return 2 * M_PI * $this->radius;
    }
    public function getDiameter() 
    {
        return 2 * $this->radius;
    }
    public function draw() 
    {
	//...    
    }
 }
І після:
class CircleData 
{
    private $radius;
    private $color;
    private $origin;
    public function __construct($radius, $color, $origin) 
    {
        $this->radius = $radius;
        $this->color = $color;
        $this->origin = $origin;
    }
    public function getRadius() 
    {
        return $this->radius;
    }
    public function getColor() 
    {
        return $this->color;
    }
    public function getOrigin() 
    {
        return $this->origin;
    }
}

class Circle 
{
    private $circleData;
    public function __construct($radius, $color, $origin) 
    {
        $this->circleData = new CircleData($radius, $color, $origin);
    }
    public function getCircumference() 
    {
        return $this->circleData->getRadius() * M_PI;
    }
    public function getDiameter() 
    {
        return $this->circleData->getRadius() * 2;
    }
    public function draw() 
    {
	//...
    }
}

Застосування

ред.

Пов'язані шаблони проєктування

ред.

Посилання

ред.