Капсулиране (програмиране)

език за програмиране

В езиците за програмиране капсулиране се използва за обозначаване на едно от две свързани, но различни понятия, а понякога и в комбинация[1][2]:

  • Механизъм за ограничаване на директен достъп до някои от компонентите на обектите.[3][4]
  • Езикова конструкция, която улеснява комбинирането на данни с методи (или други функции), извърващи операции върху тези данни.[5][6]

Някои изследователи и учени на езиците за програмиране използват първото значение на термина "капсулиране" самостоятелно или в комбинация с второто значение като отличителна характеристика на обектно ориентираното програмиране, а в някои езици за програмиране, които осигуряват лексическо затваряне, капсулирането е тяхна съществена характеристика.

Второто определение е продиктувано от факта, че в много обектно ориентирани езици скриването на компоненти не е автоматично или може да се презаписва. По този начин, укриването на информация се определя като отделно понятие от тези, които предпочитат второто определение.

Характеристиките на капсулирането се осъществяват чрез класове в повечето обектно ориентирани езици за програмиране, въпреки че има и други варианти.

Механизъм за скриване на информацията

редактиране

Капсулирането може да се използва за да скрива полета и методи на даден клас. Според тази дефиниция капсулирането означава, че вътрешното представяне на обект не се вижда извън него. Като правило, само собствените методи на даден обект могат директно да виждат или променят поле. Някои езици, като smalltalk и Руби, позволяват достъп само чрез методите на обекта, но повечето от останалите езици (например С++, С#, Делфи и Java) предлагат на програмиста възможност да избере какво да остане скрито, обикновено чрез ключови думи като public и private. Стандартът ISO в C++ се отнася до protected, private и public като „модификатори за достъп“, които „не крият информация“. Скриването на информация се осъществява чрез предоставяне на крайната версия на изходния код, който се свързва чрез заглавен файл.

Почти винаги има начин да се заобиколи тази защита – обикновено чрез отражение в API (Ruby, Java, C# и т.н.), понякога чрез механизъм като изопачаване на името (name mangling) в Python или специални запазени думи като friend в C++.

По-долу е даден пример на C#, който показва как достъпът до полето с данни може да бъде ограничен чрез използване на запазената дума private:

class Program {
	public class Account {
		private decimal accountBalance = 500.00m;

		public decimal CheckBalance() {
			return accountBalance;
		}
	}

	static void Main() {
		Account myAccount = new Account();
		decimal myBalance = myAccount.CheckBalance();

		/* This Main method can check the balance via the public
		* "CheckBalance" method provided by the "Account" class
		* but it cannot manipulate the value of "accountBalance" */
	}
}

По-долу е даден пример на Java:

public class Employee {
    private BigDecimal salary = new BigDecimal(50000.00);

    public BigDecimal getSalary() {
        return salary;
    }

    public static void main() {
        Employee e = new Employee();
        BigDecimal sal = e.getSalary();
    }
}
class Account {
    /**
     * How much money is currently in the account
     * @var float
     */
    private $accountBalance;

    /**
     * @param float $currentAccountBalance Initialize account to this dollar amount
     */
    public function __construct($currentAccountBalance) {
        $this->accountBalance = $currentAccountBalance;
    }

    /**
     * Add money to account
     * @param float $money Dollars to add to balance
     * @return void
     */
    public function deposit($money) {
        $this->accountBalance += $money;
    }

    /**
     * Remove money from account
     * @param float $money Dollars to subtract from balance
     * @throws Exception
     * @return void
     */
    public function withdraw($money) {
        if ($this->accountBalance < $money) {
            throw new Exception('Cannot withdraw $' . $money . ' from account as it contains $' . $this->accountBalance);
        }
        $this->accountBalance -= $money;
    }

    /**
     * Get current account balance, that takes all additions and subtractions into consideration.
     * @return float
     */
    public function getAccountBalance() {
        return $this->accountBalance;
    }
}

// Create a new object from the Account class with a starting balance of $500.00
$myAccount = new Account(500.00);

// We have clearly defined methods for adding and subtracting money from the Account
// If we didn't have a method for withdraw(), nothing would prevent us from withdrawing more money than was available in the account
$myAccount->deposit(10.24);
$myAccount->withdraw(4.45);

// Get the current balance
$accountBalance = $myAccount->getAccountBalance();
echo 'My Account Balance: $' . $accountBalance; // 505.79

// Our code forbids us from withdrawing more than we have
$myAccount->withdraw(600.00); // Exception Message: Cannot withdraw $600 from account as it contains $505.79

Капсулирането също така може да се прилага в езици, които не са обектноориентирани. В C, например, структурата може да бъде обявена в публично API (т.е. заглавен(header) файл) за набор от функции, които оперират върху елемент от данни, съдържащ полета на обекта, които не са достъпни за ползвателите на API:

// Header file "api.h"

struct Entity; // Opaque structure with hidden members

// API functions that operate on 'Entity' objects
extern struct Entity * open_entity(int id);
extern int process_entity(struct Entity *info);
extern void close_entity(struct Entity *info);

Ползвателите извикват API функции за алокиране, опериране върху тях и освобождаване на обекти от непрозрачен тип данни (opaque data type). Съдържанието на този тип са известни и са на разположение само за имплементацията на функциите на API-то, а ползвателите не могат директно да се обърнат към неговото съдържание. Изходният код за тези функции определя действителното съдържание на структурата:

// Implementation file "api.c"

#include "api.h"

// Complete definition of the 'Entity' object
struct Entity {
    int     ent_id;         // ID number
    char    ent_name[20];   // Name
    ... and other members ...
};

// API function implementations
struct Entity * open_entity(int id)
{ ... }

int process_entity(struct Entity *info)
{ ... }

void close_entity(struct Entity *info)
{ ... }

Историческо значение

редактиране

Целта на капсулирането е да се намалят конфликтите между променливи с еднакви имена и да се групират заедно свързаните методи (функции) и свойства (променливи), за да се състави обектен клас. Тази практика подобрява подредеността и четливостта на първичния код.

Обща дефиниция

редактиране

Капсулирането е един от четирите основи на ООП (обектно ориентирано програмиране). Капсулиране означава обединяване на данни с методи, които работят върху данните.[7] Капсулирането се използва за скриване на стойността или състоянието на структурирани данни на обекта вътре в класа чрез предотвратяване на неупълномощен директен достъп до него. Публично достъпните методи обикновено се предоставят от класа (така наречените гетъри(getter) и сетъри(setter)) за осъществяване на достъп до стойностите на полетата на обекта.

Този механизъм не е уникален за обектно ориентираното програмиране. Имплементацията на абстрактни типове данни, например, модули, предлагат подобна форма на капсулиране. Това сходство се дължи на факта, че двете понятия се основават на една и съща математическа основа – типизация на данните.[8]

Капсулиране и наследяване

редактиране

Авторите на „Design Patterns“[9] обсъждат напрежението между наследяването и капсулирането много подробно и заявяват, че според техния опит дизайнерите на класовете използват прекалено много наследяването. Опасността е формулирана по следния начин:

тъй като наследяването разкрива на наследника реализацията на родителя в подробности, то нарушава капсулирането.

Външни препратки

редактиране
  1. Michael Lee Scott, Programming language pragmatics, Edition 2, Morgan Kaufmann, 2006, ISBN 0-12-633951-1 p. 481: „Encapsulation mechanisms enable the programmer to group data and the subroutines that operate on them together in one place, and to hide irrelevant details from the users of an abstraction.“
  2. Nell B. Dale, Chip Weems, Programming and problem solving with Java, Edition 2, Jones & Bartlett Publishers, 2007, ISBN 0-7637-3402-0, p. 396
  3. John C. Mitchell, Concepts in programming languages, Cambridge University Press, 2003, ISBN 0-521-78098-5, p.522
  4. Pierce, Benjamin. Types and Programming Languages. MIT Press, 2002. ISBN 0-262-16209-1.
  5. Wm. Paul Rogers, Encapsulation is not information hiding Архив на оригинала от 2013-10-29 в Wayback Machine., JavaWorld.com, 05/18/01
  6. Thomas M. Connolly, Carolyn E. Begg, Database systems: a practical approach to design, implementation, and management, Edition 4, Pearson Education, 2005, ISBN 0-321-21025-5, Chapter 25, „Introduction to Object DMBS“, section „Object-oriented concepts“, p. 814
  7. Rodgers, Wm. Paul. Encapsulation is not information hiding // JavaWorld. Архивиран от оригинала на 2014-06-08. Посетен на 15 март 2014.
  8. Pierce (2002), Section 24.2 „Data Abstraction with Existentials“
  9. Gamma, Erich, Helm, Richard, Johnson, Ralph. Design Patterns. Addison-Wesley, 1994. ISBN 0-201-63361-2.