CSC/ECE 517 Fall 2011/ch1 2b ns

From Expertiza_Wiki
Jump to navigation Jump to search

Access Control in Object-Oriented Languages

Introduction

A fundamental precept of O-O systems is that an object should not expose any of its implementation details. In other words, access to an object’s functionality must be tightly regulated. The access control policies of any object-oriented language specify the rules and capabilities for enforcing this regulation. Thus, the concept of Access control ties in deeply with the underlying principles of Object Oriented Programming.


Purpose of Access Control

Object-oriented programming fundamentally revolves around the principle of Data Hiding and Encapsulation. Data Hiding is the practice of securing data by constraining access to it, and Encapsulation ensures that the secure data (and the respective member functions) are bundled together as an individual module. All object oriented languages thus need to control the level of access to their critical data (and functions) which is achieved using Access Modifiers or Access Specifiers.

The need to strictly control who can access what is very crucial in Software development. For example, if you make an instance variable 'public', then you can't change the field as the class evolves over time since you might break any external code that uses the field. This is because outside code having complete access to this field, has tied itself indelibly to the inside implementation of the (former) class and is now closely dependent on it. Changing the said field thus entails changing all the other code that have a dependency on it, which is cumbersome, unwieldy and contrary to the principles of an Object Oriented approach which strives for Low Coupling.

Thus, the ‘Implementation hiding principle’ leads to a good acid test of an O-O system's quality: Can you make massive changes to a class definition—even throw out the whole thing and replace it with a completely different implementation—without impacting any of the code that uses that class's objects? This sort of modularization is the central premise of object orientation and makes maintenance much easier. Without implementation hiding, there's little point in using other O-O features. Regulation of access - Access Control - is at the heart of Object oriented programming<ref>http://www.javaworld.com/javaworld/jw-09-2003/jw-0905-toolbox.html</ref>.

Evolution of Access Control and proliferation of Accessor methods

Access control in O-O languages has evolved over time to become highly complicated <ref>http://www.exforsys.com/tutorials/oops/the-use-of-access-specifiers-in-object-oriented-programming.html</ref> because, the requirements of computing and software development have ballooned considerably in last decade and a half. Complex software requirements mandates complexity in languages, and this has indeed affected the access control policies, which have evolved into something comprehensive, yet - at times - convoluted. Unfortunately today's developers often lose sight of the fundamental motivation behind the existence of extensive access control and lose out on much of the benefits of having a highly cohesive, low coupled system<ref>http://en.wikipedia.org/wiki/Coupling_(computer_programming)</ref>.

To elaborate, it has become somewhat of an involuntary practice for present programmers to make all data members of a class private and provide accessor methods (getters/setters) to all of them (data members). Developers have come to accept this to be a contemporary programmatic-paradigm and by supplying setters and getters for everything, defeat the very purpose of making class-fields private. A long time ago programmers discovered that reducing the scope (visibility) of data as much as possible leads to more reliable and maintainable code<ref>http://typicalprogrammer.com/?p=23</ref>.

Every getter and setter in one’s code represents a failure to encapsulate and creates unnecessary coupling. A profusion of getters and setters is a sign of a poorly-designed set of classes. Java<ref>http://www.eclipse-blog.org/eclipse-ide/auto-generating-getters-and-setters.html</ref> and C#<ref>http://forums.asp.net/t/941864.aspx/1</ref> developers have IDEs that generate getters and setters automatically, implying that accessors are a good idea and thus contribute to that popular belief. That is not to say that one must completely abstain from accessors, but rather re-analyze and - if required - redesign the class structure to involve as little exposure of its data members as can be achieved.

Access Control: A typical example

Empirically, the data members of a class are held private, and the member functions (public) act as the end points of this class. Via these public methods, one can access the ‘state’ of an object. The following example illustrates this point and shows how access to the data members of a class are specified and controlled by the access modifiers.

UML Diagram of a BankAccount Class.

Typical UML diagram of BankAccount.

The “(-)/(+)” preceding the members of this class represent the associated access levels. (-) indicates private and (+) indicates public. The data members ‘accountNumber’, ‘routingNumber’ and ‘balance’ are maintained as private. Thus, an instance of a ‘Customer’ Object (say) cannot directly access or modify this data. Customer has to interact with the public behavior bundled in this module which allows him to view his account information and/or withdraw/deposit money i.e. his visibility of the class is only restricted to that which is made public by the Class.

Using such access specifiers, we can control the level of security and visibility associated with the members of a class. Thus, an Access Specifier can be roughly defined as ‘A keyword applied to a variable, method, etc. that indicates which other parts of the code are permitted to access it’.

Common Access Specifiers in O-O languages

Although all access specifiers essentially provide the same functionality, their semantics differ among different O-O langs. Here is how access control is implemented in some of the more common O-O langs.

C++

  • private:All members of the Class that are declared as private can only be accessed by the class's member functions and friends of the class.
  • protected:All members of the Class that are declared as protected can be accessed by that class's member functions and friends (classes or functions) of the class. Moreover, classes derived from the class also have access to these elements.
  • public:Any class or method can have access to these type of elements.

Java

Java offers four access specifiers, listed below in order of decreasing accessibility:

  • public: All public classes, methods, and fields are the least secure. The Public access specifier must be used if you explicitly want to offer access to these entities and if this access cannot do any conceivable harm.
  • protected: Protected methods and fields can only be accessed within the same class to which the methods and fields belong, within its subclasses, and within classes of the same package. Use of protected access specifier enables a class's subclasses to have access to the method or field, but not for unrelated classes.
  • default (no specifier): Default access specifier is Java’s fail-safe way to handle security. Via the default access specifier, a class, method, or field will be accessible from inside the same package to which the class, method, or field belongs, but not from outside this package. Default access is thus suited when the source code is compartmentalized and organized into packages.
  • private: Private access specifier is a way to secure vital members of a class. Private methods and fields can only be accessed within the same class to which the methods and fields belong. Private methods and fields are not visible within subclasses and are not inherited by subclasses.

Ruby

Ruby gives you three levels of protection, listed below in order of decreasing accessibility:

  • Public:Public methods can be called by everyone - no access control is enforced. A class's instance methods are public by default.
  • Protected:Protected methods can be invoked only by objects of the defining class and its subclasses. Access is kept within the family.
  • Private:Private methods cannot be called with an explicit receiver - the receiver is always self. Thus private methods can be called only in the context of the current object and cannot be invoked by another object's private methods.

Access control in Ruby is determined dynamically, as the program runs, not statically. You will get an access violation only when the code attempts to execute the restricted method<ref>http://rubylearning.com/satishtalim/ruby_access_control.html</ref>.

C#

The following access modifiers specify the accessibility levels in C#

  • public:Any public method or member can be accessed by any other code in the same or different package.
  • private:The method or member can only be accessed from within the same class.
  • protected:The method or member can be accessed only from within the same Class or from within any of the subclasses.
  • internal:The type or member can be accessed by any code in the same assembly, but not from another assembly. An assembly in C# is the rough equivalent of a package in java.
  • protected internal:The type or member can be accessed by any code in the assembly in which it is declared, or from within a derived class in another assembly. Access from another assembly must take place within a class declaration that derives from the class in which the protected internal element is declared, and it must take place through an instance of the derived class type.


Summary of Access Specfiers in C++, Java, Ruby and C#:

Access Level C++ Java Ruby C#
Public Anything can access Anything can access Anything can access Anything can access
Protected Class, Subclasses and friends Class, Subclasses and classes of same package Class and Subclasses Class and Subclasses
Private Within Class Within Class Within Class Within Class
Internal -- -- -- Any Class from same Assembly
Protected internal -- -- -- Any Class from same Assembly and Subclass from any Assembly.

Examples of Access Specifiers

The following examples exhibit the use of the access specifiers and getter methofs for the aforementioned Bank Account example.

C++

class BankAccount {
	private:
	char routingNumber;
	char accountNumber;
	int  balance;
	
	public:
	BankAccount(char *, char *);
	void changeAccountNumber(char *);
	void changeRoutingtNumber(char *);
	void depositMoney(int);
	void withdrawMoney(int);
	void displayAccountInfo();
}

BankAccount::BankAccount(char *rNo, char *accNo) {
	Strcpy(routingNumber, rNo);
	Strcpy(accountNumber, accNo);
	balance = 0;
}

BankAccount::void changeAccountNumber(char *acNo){
	Strcpy(accountNumber, accNo);
}

BankAccount::void changeRoutingtNumber(char *rNo){
	Strcpy(routingNumber, arNo);
}

BankAccount::void depositMoney(int amount) {
	balance += amount;
}

BankAccount::void withdrawMoney(int amount) {
	balance -= amount;
}

BankAccount::displayAccountInfo() {
	cout << “Account Number:” << accountNumber << endl;
	cout << “Routing Number:” << routingNumber << endl;
	cout << “Account Balance:” << balance << endl;
}

All the datamambers routingNumber, accountNumber and balance are made private. This makes it impossible to directly access or change these data members from outside code. Access to these attributes are strictly through the public methods changeRoutingNumber() and changeAccountNumber(). Thus, it purely at the discretion of the programmer to allow access to these data members through such accessor functions. In the absence of such accessors, there would be no way for any code outside the class to view/modify the private attributes.


Java

public class BankAccount {
	String accountNumber;
	private String routingNumber;
	private Integer balance;
	
	public void setAccountNumber(String acNo){
		this.accountNumber=acNo;
	}
	
	public void setRoutingNumber(String rtNo){
		this.routingNumber=rtNo;
	}
	
	protected void setBalance(Integer bal){
		this.balance=bal;
	}
	
	public String getAccountNumber(){
		return this.accountNumber;
	}
	
	public String getRoutingNumber(){
		return this.routingNumber;
	}
	
	protected Integer getBalance(){
		return this.balance;
	}
	
	public void showAccountNumber(){
		System.out.println(this.accountNumber);
	}
	
	public void showRoutingNumber(){
		System.out.println(this.routingNumber);
	}
	
	public void showBalance(){
		System.out.println(this.balance);
	}
	
	public void withdraw(Integer amount){
		this.balance-=amount;
	}
	
	public void deposit(Integer amount){
		this.balance+=amount;
	}

	public static void main(String args[]){
		BankAccount Steve = new BankAccount();
		
		//public access to setRoutingNumber/getRoutingNumber
		Steve.setRoutingNumber("1234");
		String steveRtNo=Steve.getRoutingNumber();
		System.out.println(steveRtNo);
		
		//protected access to the method setBalance/getBalance
		Steve.setBalance(5000);
		System.out.println(Steve.getBalance());		
	}

}

Class BankAccount in the above Java code has public getter/setter methods to access the private data member ‘routingNumber’. Thus, we can invoke these methods using an instance of the BankAccount object: Steve. The methods setBalance()/ getBalance() are protected. As can be seen, they behave like public methods if invoked in the same package. Protected methods act like private methods if invoked by any instance of a class outside the package. However, if there is a class ‘InternationalBankAccount’ in a different package such that:

public class InternationalBankAccount extends BankAccount {
	public String internationalBankAccNo;
}
InternationalBankAccount Anna = new InternationalBankAccount();
Anna.setBalance(10000);

The above is a valid syntax and Anna can access protected member functions of the parent class. Data member ‘accountNumber’ has default access specifier. Default access specifier is used in Java to support package driven development. Thus ‘accountNumber’ can be accessed from anywhere within the package. However no classes from other packages can access ‘accountNumber’. Data member balance is a private data member and if it is invoked explicitly as follows,

Steve.balance=1000;

then the output would be an unresolved compilation error of the form:

Unresolved compilation problem: The field bankAccount.balance is not visible


Ruby

The following code illustrates the use of access specifiers in Ruby.

class BankAccount  

    def initialize(acNo, rtNo, bal)  
      @accountNumber = acNo
      @routingNumber = rtNo
     @balance = bal
    end  

    def balance
      @balance
    end 

    def routingNumber
      @routingNumber
     end 

    def accountNumber
      @accountNumber
     end

    def compare_balance(other)  
      if other.balance > balance   
        "The other object has more bank balance."  
      else  
        "This object has more bank balance."  
      end  
    end  

    protected:balance
    public:routingNumber
    private:accountNumber  

end  
Steve = BankAccount.new(713, 123, 1000)  
Anna  = BankAccount.new(923, 125, 2000)  
puts Steve.routingNumber()  
puts Steve.compare_balance (Anna) 
puts Steve.accountNumber()
Output:
123
The other object has more bank balance.
NoMethodError: private method `accountNumber' called for #<BankAccount:0x2b75320>

‘routingNumber’ is a public function. Thus, when we access object Steve’s public method ‘routing number’, we successfully get ‘123’ as the output. Furthermore, this snippet provides a mechanism for the comparison of one account holder’s balance with another. This comparison involves a call to the method ‘balance’. The object performing the comparison has to ask the other object to execute its balance method. So, balance cannot be private. With ‘balance’ changed to protected instead of private, Steve can ask Anna to execute ‘balance’, because both are instances of the same class. But if we try to call the ‘balance’ method of a bankAccount object when ‘self’ is anything other than a bankAccount object, the method will fail. A protected method is thus just like a private method, but with the exemption for those cases where the class of ‘self’ and the class of the respective object (having the method called on it) are the same. ‘accountNumber’ method is private. Thus user gets a ‘NoMethodError’ on invoking object Steve’s ‘accountNumber’ method. Initialize method in a class is private by default. Whenever the new method is invoked, initialize method is called and instances are created. If we try to invoke the ‘initialize’ method explicitly, we get the same error as in the case when we tried to invoke object Steve’s private ‘accountNumber’ method.


C#

class BankAccount 
{
	public BankAccount(string accountNumber, string routingNumber, int balance)
	{
		this.accountNumber = accountNumber;
		this.routingNumber = routingNumber;
		this.balance = balance;
	}
	
	protected string accountNumber;
	protected string routingNumber;
	private int balance;
	
	private void setAccountNumber(string acNo)
	{
		this.accountNumber=acNo;
	}
	
	private void setRoutingNumber(string rtNo)
	{
		this.routingNumber=rtNo;
	}
	
	protected initBalance()
	{
		this.balance = 0;
	}

	public void getBalance(int balance)
	{
		return this.balance;
	}
}

class Customer : BankAccount
{
	Customer(string accountNumber, string routingNumber)
	{
		this.accountNumber = accountNumber;
		this.routingNumber = routingNumber;
		initBalance();
	}
}

class Startup
{
	public static void main()
	{	
		Customer cust = new Customer(“999”, “888”, 100);
		System.console.WriteLine(cust.getBalance);	
	}
}

The Customer class inherits from the BankAccount class. All protected data members and methods of BankAccount class are accessible directly by an object of the Customer class. The private fields such as accountNumber and routingNumber are not directly accessible. Thus it is possible to call only the initBalance and getBalance methods from Customer. The initBalance method cannot be called by anyone outside the BankAccount class.

Conclusion

Access control is an integral component of object oriented languages and it underlines and emphasizes some the pricipal ideals central to the object-oriented approach. It is possible to maximize the benefit of any O-O language by adhering to, and fully exploiting the capabilities of its access specifiers. Access control is in no way a peripheral add-on or redundant feature of O-O languages, and programmers would only stand to gain by fully appreciating, respecting and understanding their presence.

References

<references/>