CSC/ECE 517 Fall 2012/ch1 1w15 rt: Difference between revisions
No edit summary |
|||
Line 445: | Line 445: | ||
|} | |} | ||
= | =Basic guidelines on the usage of Access Control= | ||
As a good programming practice, the following guidelines can be used to implement access control mechanisms. | As a good programming practice, the following guidelines can be used to implement access control mechanisms. |
Revision as of 21:34, 13 September 2012
Access Control in O-O Languages
Introduction to Access control
In object oriented programming, access control refers to the control of visibility of data and methods. The main idea behind access control is to restrict access to certain parts of the code to external entities. Access control implements encapsulation by providing varying levels of access to code. The use of access control becomes necessary in any practical environment where selective access is a key criterion.
For example, only the instructor can be allowed to modify grades of students but all students should be given access to retrieve and view their respective grades.
Access control in different object oriented languages and the underlying details will be covered in this topic.
Overview of Access Control Mechanisms
Each O-O language has a specific implementation of access control mechanisms. However, a plethora of the most widely used o-o languages share similar features while implementing access control. Access control mechanisms are applicable to class members like variables and functions. Some level of access control can be applied at the class level. The 3 basic types of access control implementation which is common to some static and dynamic O-O languages [like C++, C#, Java and Ruby] are
Public : Methods or attributes that are declared to be public can be accessed from any class. This is the least restrictive access control method. For example, we may want everyone in a company to know the names of people working along with them. Thus public access is given to employee's name.
Private : This mechanism makes the data or method to be visible only in the class in which it is declared. The support is not extended to sub classes. This is the most restrictive access control method. In any application, methods which implement the core functionality or require administrator access are declared to be private thereby enforcing limited access. For example, only a manager can be given the authority to review an employee’s performance and award bonus and promotions.
Protected : The use of protected access allows access of data and methods to be modified by members belonging to the same family. This means that this support is extended to subclasses. Protected access control can be used to support the single interface, multiple implementation concept i.e, inheritance. For example, a super class may have a protected method called ComputeArea(). We can have many subclasses like Triangle, Rectangle etc inherit the super class and override the method ComputeArea() to provide their own specific implementation. In a more practical scenario, a person might be allowed to view the medical history of immediate family members. However, people outside the family will not be allowed access to view this medical history.
Implementing Access Control
Programming languages implement access control in different ways. We present a few of the languages that use access control and show their implementation.
Java
In Java, access control is achieved using the modifiers public, private and protected. If a class is not declared with any modifier, then the default access becomes package-private i.e, any object within the package should be able to access this class.
Public Keyword – Other classes can modify public fields unless its declared as final. The syntax for declaring a variable/method public is as follows
public int x; public void print_Hello()
In the example shown below, the variable studentName is public and hence can be accessed in another class Student2. Similarly, public methods like getName() etc become accessible by other classes.
Private Keyword – This keyword makes the class members private. Private members can only be accessed from within the class. They are not visible even in subclasses. However, visibility and access of private members is extended to nested classes. The syntax for declaring a variable/method private is as follows
private String EmpID; private void getEmpDetails()
In the example code below, the method setID is declared to be private. Hence, it cannot be accessed in the subclass of the class Student.
Protected Keyword – This keyword when used before a class member makes that member visible to the elements residing in that class and its subclasses. However if the subclass is in a different package, then the protected member is visible only through inheritance. It cannot be accessed by the object of the parent class as the definition is in another package. The syntax for declaring a variable/method protected is as follows
protected int sides; protected void setArea(int sides)
In the example code below, the method setName is declared as protected. Hence, it can be accessed in the subclass NewStudent.
Default access level/Package – When no specific access control keyword is given, then the method or variable becomes accessible to all classes under a particular package. In the following example, the integer “packageVar” becomes accessible to all classes included in the package ‘TestPackage1’. Hence, it can be modified in the class Student2.
package TestPackage1 public class Student { public String studentName; public int studentID; int packageVar; public String getName() { return studentName; } public int getID() { return studentID; } protected void setName(String s) { studentName = s; } private void setID(int y) { studentID = y; } public static void main(String[] args) { Student Stu1 = new Student(); Stu1.setName("ABCD"); Stu1.setID(1230013); System.out.println(Stu1.getName() + Stu1.getID()); } } public class NewStudent extends Student { public static void main(String[] args) { NewStudent NewStu1 = new NewStudent(); NewStu1.setName("EFGH"); //Can be accessed NewStu1.setID(350); // Cannot be accessed as setID is private System.out.println(NewStu1.getName()); } } public class Student2 { public static void main(String[] args) { Student Stu2 = new Student(); Stu2.studentName = "Mickey"; //Public variable of another class can be accessed Stu2.packageVar = 5; //Default access level } }
C++
In C++, we have the three modifiers presented in the overview along with a special type of access control called friend. The behavior of public, private and protected modifiers is similar to its Java counterparts.
Friend - A function or class can access the private and protected members of a class if it is declared as a friend of that class.
class Number { int a,b; friend int canAccess(Number numb); // Declaring canAccess() to be a friend public: int add(int,int); protected: int diff(int,int); private: int prod(int,int); int add(int a,int b) { return a+b; } int diff(int a,int b) { if (a>b) return a-b; else return b-a; } int prod(int a,int b) { return a*b; } }; public static void main() { Number num; int a=10, b=20; cout << "Sum =" << num.add(a,b); cout << "Difference =" << num.diff(a,b); //cout << "Product = " << num.prod(a,b); // this will throw an error if the comments are removed } // Friend function int canAccess(Number numb) { cout<< "Product for a friend = " << numb.prod(10,20) }
More about friend functions
If a class is declared as a friend in another class, the friend class can access all the private members of the class. This can also be limited in such a way that only few private members of the class can be accessed. By using friend functions or classes, we can provide a flexibility of letting a friend class access restricted members of a class and still keeping the restricted members closed for access by any other class. This will be useful in a few cases.
Ruby
Ruby provides the three levels of access controls, private, public and protected with the default being public access. The behavior of public and protected access control is similar to other languages like C++ and Java.
However, private methods can be called only within the context of the current object. Other object’s private method cannot be invoked. Private methods can be overridden by the sub-classes.In most cases, private methods are used to provide internal functionality to the class. So we need to use a cautious approach in using private methods. If they are overridden by the subclass, we tend to get unexpected behavior in the program.
Note: Private methods cannot be called outside of the class. Private methods can be called only using the “send” method.
class Number def initialize(a,b) @number1 = a @number2 = b end def num1 @number1 end def num2 @number2 end def add(a) puts a.num1+a.num2 end def diff(a) puts a.num1-a.num2 end public:add public:diff protected:num1 #private:num2 end Num1=Number.new(50,60) Num1.add(Num1) Num1.diff(Num1)
This program runs successfully and prints the output as long as the private method is not accessed. Here, the private method is num2 and should not be invoked in the current context of the object.
in `add': private method `num2' called for #<Number:0x436e18 @number1=50, @number2=60> (NoMethodError)
However, the private method num2 can be accessed using the send method. The syntax for accessing the method is a.send(:num2)
C#
It has 5 specific levels of access control at the class member level.
1. Public : Public access is the least restrictive access level and class members can be accessed anywhere.
2. Private : Private is the most restrictive access level where members are accessed only in the body of the class in which they are declared. This support is also extended to nested classes.
3. Protected : The protected keyword is a member access modifier i.e, it cannot be applied at a class level. A protected member is accessible from within the class in which it is declared, and from within any class derived from the class that declared this member.
class Number { public int x =5; public int y =10; public int add() { return x+y; } protected int diff(){ return y-x; } private int prod(){ return x*y; } } class Number1 { public static void Main() { Number num = new Number(); num.add(); //access to add num.diff(); // can't as diff is declared protected num.prod(); // can't access as prod is private } } class Number2 : Number { public static void Main() { Number2 num2 = new Number2(); num2.diff(); // access to protected member } }
4. Internal : It is an access modifier for types and type members. Internal members are accessible only within files in the same assembly.
5. Protected Internal: This access control restricts access of members to the class, subclass within the assembly. The difference between protected internal and internal is that, a subclass of the class derived in another assembly can also access the protected internal method.
File1.cs: class Number3 { internal int x =0; protected internal int y = 0; } File2.cs class Number4 : Number3 { public static void Main() { Number4 num4 = new Number4(); num4.x = 4 // error, as x is declared as internal num4.y = 5 // Allowed. As y is protected internal. } }
PHP
In PHP the visibility of a property or method can be defined by prefixing the declaration with the keywords public, protected or private
1. Private : Private access specifier is used to hide the data member or member function to the outside world so that only the class that defines such data member and member functions have access to them.
2. Public : public access specifier allows the outside world to access/modify the data members directly unlike the private access specifier. In pHP all data members and member functions are public by default.
3. Protected : A protected access specifier is mainly used with inheritance. A data member or member function declared as protected cab be accessed by its class and its base class but not from the outside world (i.e. rest of the script). Thus a protected data member is public for the class that declares it and it’s child class; but is private for the rest of the program.
<?php class bankAccount { private $accountNumber; private $routingNumber; protected $balance; # setter methods for accountNumber allows public acccess to it public function setAccountNumber($acctNumber) { $this->accountNumber = $acctNumber; } # setter methods for routingNumber allows public acccess to it public function setRoutingNumber($routeNumber) { $this-> routingNumber = $routeNumber; } #Getter method allows accountNumber to be read publicly public function getAccountName() { return $this->accountName; } # getter methods for routingNumber to be read publicly public function getRoutingNumber() { return $this-> routingNumber; } } class savingsAccount extends BankAccount{ # setter method for balance public function setBalance($bal;) { $this-> balance = $bal; } # retrieve balance..note that balance is a protected variable. function getBalance() { return $this-> balance } } ?> #Creating objects and initializing variables $ newAcct -> setAccountNumber(“1234343455”) $ newAcct -> setRoutingNumber(“23DDAS25”) $ newAcct -> setBalance(“1234343455”) #Accessing public data member echo "Savings Account Number is " . $ newAcct ->getAccountNumber() . '<br>'; echo "Routing number is = " . $ newAcct -> getRoutingNumber() . '<br>'; #Accessing protected data member echo "Balance in account is is = " . $ newAcct -> getBalance() . '<br>';
Python
Python implements access control in a different way from most other languages in that they don’t have specific access control modifiers. By default all members are public and we precede the member name with ‘__’ (a double underscore) to indicate if the member is private. This feature of python is called as Name Mangling.
There is no feature in python to implement the protected access control.
class Number: def __init(self,a,b): // this is a private method self.a = a self.b = b def add(): print self.a + self.b def diff(): print self.b - self.a Num1=Number(50,60) Num1.add() Num1.diff()
Note that in python the __init method is made to be private as default while programming.
More on Name Mangling in Python
Name mangling is a feature of python using which we indicate whether a member is private by just adding underscores, instead of using key words. When we program or debug, it is easier to note that the member is private and thus refrain from accessing it.
Eiffel
Eiffel has a different approach to access control compared to the previous object oriented languages like C++ and Java. It uses selective export, which means that different methods and attributes can have different access levels. Also, we can individually specify the access level for each method or attribute.
class Number feature{NONE} -- indicates nothing can access this code from outside of the class (private) a: INTEGER b: INTEGER initialize is do a := 10 b := 20 end end feature{ANY} -- indicates this method can be accessed from anywhere outside of the class(public) add: INTEGER is do Result := a+b end end feature{Number} -- indicates that this method can be accessed only by members of the class Number diff: INTEGER is do Result := b-a end end
More on feature:
Using feature, we can provide varying levels of access to a code in the class. And also we can provide the names of classes that are only allowed to access that part of the code. In the above example, the different access levels that features provide are explained in the comments.
Comparison among different O-O languages
Java | C++ | C# | PHP | Ruby | Python | Eiffel | |
---|---|---|---|---|---|---|---|
Public | Can be accessed outside of class | Can be accessed outside of class | Can be accessed outside of class | Can be accessed outside of class | Can be accessed outside of class | Can be accessed outside of class | Can be accessed outside of class |
Protected | Access within the class and sub-classes | Access within the class and subclasses | Access within the class and subclasses | Access within the class and by inherited and parent classes. | Access within the class and subclasses | No protected access | Access can be given to only a few of the classes. |
Private | Cannot be directly accessed from outside the class. Can be accessed by nested classes. | Cannot be directly accessed from outside the class | Cannot be directly accessed from outside the class | Cannot be directly accessed from outside the class | Cannot be directly accessed from outside the class | Cannot be directly accessed from outside the class | Cannot be directly accessed from outside the class |
Special feature | Default access is package. Any class in a package can access if no modifier is given | friend functions(access of private to a specific friend class or method) | internal and protected internal | property declared with the 'var' keyword is considered 'public'. | send method is used to access private members | Name Mangling(use of underscores to indicate access level) | Selective export using "feature" |
Basic guidelines on the usage of Access Control
As a good programming practice, the following guidelines can be used to implement access control mechanisms.
1. Use public access only if an interface for a class so that the data can be sent or modified based on external requirements.
2. Use of private is advisable whenever we program the internal structure of the program, like the constructors. Some languages, like Ruby make their variables private by default so that modifications from external entities are avoided.
Design patterns for handling Access Control
The two most commonly used design patterns to handle access control are the private class data design pattern and the decorator design pattern.
Decorator design pattern
Decorator pattern is a design pattern attaches additional responsibilities to an object dynamically. It is required when we need to restrict access to an object’s property or method. The access is restricted based upon some set of rules or parallel set of rules. In such cases instead of having access control in the original object we leave the object unchanged and unaware of any restrictions. Rather we wrap the object in an access control decorator object.
We start with an interface which creates a blue print for the class which will have decorators, then implement that interface with basic functionalities. The constructor of this class assigns the interface type instance to that attribute. This class is the decorator base class. Now you can extend this class and create as many concrete decorator classes. The concrete decorator class will add its own methods. After/before executing its own method the concrete decorator will call the base instance’s method. The key to this decorator design pattern is the binding of method and the base instance happens at runtime based on the object passed as parameter to the constructor.
An example of decorator design pattern would be ice cream. You can create a basic ice-cream and then add flavors to it as you want. This will change the taste of the basic ice cream.
The UML diagram is as follows:
The code for basic ice cream class
public class SimpleIcecream implements Icecream { @Override public String makeIcecream() { return "Basic Icecream"; } }
And the decorator class will be
abstract class IcecreamDecorator implements Icecream { protected Icecream specialIcecream; public IcecreamDecorator(Icecream specialIcecream) { this.specialIcecream = specialIcecream; } public String makeIcecream() { return specialIcecream.makeIcecream(); } }
We can add a flavor as:
public class VanillaDecorator extends IcecreamDecorator { public VanillaDecorator(Icecream specialIcecream) { super(specialIcecream); } public String makeIcecream() { return specialIcecream.makeIcecream() + addVanilla(); } private String addVanilla() { return " + Vanilla flavored"; } }
If we want to add another flavor. We can do so by the code below
public class StrawberryDecorator extends IcecreamDecorator { public StrawberryDecorator(Icecream specialIcecream) { super(specialIcecream); } public String makeIcecream() { return specialIcecream.makeIcecream() + addStrawberry (); } private String addStrawberry () { return " + Strawberry flavored"; } }
We execute the pattern in the following fashion:
public class TestDecorator { public static void main(String args[]) { Icecream icecream = new StrawberryDecorator (new VanillaDecorator (new SimpleIcecream())); System.out.println(icecream.makeIcecream()); } }
The output will be Basic Icecream + Vanilla flavored + Strawberry flavored.
Similarly we can add as many flavors like chocolate or strawberry on top of basic ice cream. Thus we add additional responsibilities dynamically and restrict access to the objects properties.
Private class data design pattern
A class may expose its attributes (class variables) to manipulation when it is no more desirable, for example after the construction is done. Using the private class data design pattern helps in preventing this unwanted manipulation. A class may also have one-time mutable attributes that cannot be declared final. Using this design pattern allows one-time setting of those class attributes. The motivation for this design pattern comes from the design goal of protecting class state by minimizing the visibility of its attributes (data).
The intent of this private class data design pattern is to provide the following:
- Control write access to class attributes
- Separate data from methods that use it
- Encapsulate class data initialization
- Providing new type of final - final after constructor
The private class data design pattern seeks to reduce exposure of attributes by limiting their visibility. It reduces the number of class attributes by encapsulating them in single Data object. It allows the class designer to remove write privilege of attributes that are intended to be set only during construction, even from methods of the target class.The private class data design pattern solves the problems by extracting a data class for the target class and giving the target class instance an instance of the extracted data class. These can be achieved by following these procedures
1. Create data class. Move to data class all attributes that need hiding
2. Create in main class instance of data class
3. Main class must initialize data class through the data class's constructor
4. Expose each attribute (variable or property) of data class through a getter
5. Expose each attribute that will change in further through a setter
The following C# code illustrates an opportunity to use the private class data design pattern. The attributes radius, color, and origin should not change after the Circle() constructor. Note that the visibility is already limited by scoping them as private, but doing methods of class Circle can still modify them. Although marking attributes of classes as const (or final or ReadOnly in other programming languages) restricts their manipulation, the attributes above are set in the constructor and hence cannot be marked as such.
The excess exposure of the attributes creates a type of (undesirable) coupling between methods that access those attributes. To reduce the visibility of the attributes and thus reduce the coupling, we implement the private class data design pattern, as follows:
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) { //... } }
The Circle class in the resulting code has an attribute of type CircleData to encapsulate the attributes previously exposed to all of the methods of the class Circle. That encapsulation prevents methods from changing the attributes after the Circle() constructor. Note, however, that any method of Circle can still retrieve the values of the encapsulated attributes.
Advantages and disadvantages of Access Control
Access control means exerting control over who can interact with a particular resource. Hence the proper usage of access control is desirable as it comes with its own merits and demerits.
Advantages of Access Control
Using access control allows a programmer to better protect the members and methods of an object from bad use. This process of protecting data and methods has the following advantages.
Makes maintenance of application easier: Complex and critical applications are difficult to maintain. The cost associated with maintaining the application is higher than that of developing the application properly. The concept of encapsulation which bundles data and related functions together as a unit called class, thus makes maintenance much easier on the class level. The rest of the code is not cluttered up by these data and methods and it also ensures that the rest of the code doesn’t interfere with them
Improves the understandability of the application: Keeping the code clean by using access control methods also leads to self-documentation and understanding. Other programmers having to use that class can figure out more clearly as to which part of the class they are supposed to access and which part they are not
Enhances Security: The access specifier acts as the key strength behind the concept of security and provides access to members of class as needed by users. This prevents unauthorized access. If an application needs to be extended or customized in later stages of development, the task of adding new functions becomes easier without breaking existing code or applications, thereby giving an additional security to the existing application
Disadvantages of Access Control
The disadvantages of access control are not really significant when the advantages are laid down. Nevertheless there are few shortcomings.
Extra lines of code: To implement access control we have to write more lines of code and there is no point in using them when the program requirements are that, it need not have any encapsulation and information hiding
Adds overhead: Access control can be avoided when the use of the system can be significantly simplified by allowing encapsulation to be violated under certain conditions. For example with ad-hoc queries the need of encapsulation is reduced as issues such as maintainability are not critical. Also there are cases where wrong/improper use of access control methods can cause the program to behave abnormally.
Increases compiler complexity: In order to provide techniques for encapsulation, compilers must implement late binding for methods and it makes the compiler more complex
Conclusion
Access control provides powerful mechanism to implement various OO features and it can find its use in several applications. Having a proper understanding of different access control methods and using the proper access control techniques can help to control ambiguity and leads to easy maintainability of the code. However, if not used with caution, access control can lead to unexpected behavior. It is always important to use the right access control to avoid ambiguity and ease the maintainability of code.
References
- Object Oriented Programming
- Encapsulation
- Access Control in Java
- Member Access Control in C++
- C++ friend functions
- Implementing friend functions
- Access Modifiers in C#
- Programming Ruby
- Access Control in Ruby
- Name Mangling in OO Langugages
- Name Mangling in Python
- Class Objects in Python
- Eiffel for Beginners
- More on Eiffel
- Into Eiffel By Ian Joyner
- Static and Dynamic Typing