Енкапсулация (програмиране)

от Уикипедия, свободната енциклопедия
Направо към навигацията Направо към търсенето

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

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

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

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

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

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

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

Почти винаги има начин да се заобиколи тази защита – обикновено чрез рефлекшън (отражение) в 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, 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. Посетен на 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.