• No results found

INF 15 Class Responsibility

N/A
N/A
Protected

Academic year: 2020

Share "INF 15 Class Responsibility"

Copied!
40
0
0

Loading.... (view fulltext now)

Full text

(1)

Class Responsibility

Subjects:

Introduction

Letting the class protect itself through access control

Introducing the property, a specialized kind of method

Allowing an object to initialize itself via the constructor

Defining multiple constructors for the same class

(2)

Introduction

A class must be held responsible for its actions.

To be held responsible for its actions, a class must ensure that its initial state is correct

and then control its subsequent state so that it remains valid. C# provides both these capabilities.

(3)

Scope

An item’s scope includes the code that can use the item. To make programs manageable, code can be divided in several ways. For example, different structures or classes can contain methods that contain code. The scope of a program item (variable, routine, method, and so forth) is generally determined by the level at which it is defined.

For example, if a variable is declared inside a routine, then its scope is the routine and only code inside that routine after the variable’s declaration can use the variable. This is called routine scope (or method scope, procedure scope, and so on, depending on what term you use to name a routine).

At a higher level, a program can define routines, variables, types, and other items inside a structure, class, or code module, but not inside any routine. In that case, all the code in the structure, class, or code module can use the item. This is called structure scope, class scope, or module scope.

(4)

Scope

All the class scope items are usable by any code inside the class. They also may be usable by code outside the class, depending on their accessibility.

The ToString method declares a string variable named result. This variable is declared inside the

(5)

Accessibility

A code item’s accessibility determines what code (if any) outside the item’s scope can access the item. For example, a public method declared inside a class can be called by code outside the class. Items with structure, class, and code module scope can have accessibilities that provide access to pieces of code outside their scope. A programming language lets the code use accessibility keywords to determine how accessible the code is.

(6)

Accessibility

The internal (or Protected) keyword makes an item usable only to code in the same assembly. In .NET applications, an assembly is the smallest self-contained unit of compiled code. An assembly can be a complete application or a library that can be called by other applications.

Private accessibility means that only code in the same structure, class, or code module can use the item, but it does not mean that the code must be running in the same instance of the structure or class.

Access Modifiers

From within a class, any function member can access any other member of the class by simply using that member’s name.

The access modifier is an optional part of a member declaration that specifies what other parts of the program have access to the member. The access modifier is placed before the simple declaration forms.

(7)

Private

and

Public

Access

Private members are accessible only from within the class in which they are declared— other classes cannot see or access them.

Private access is the default access level, so if a member is declared without an access modifier, it is a private member.

You can also use the private access modifier to explicitly declare a member as private. There’s no semantic difference between declaring a private member implicitly as opposed to explicitly. The forms are equivalent. Еhe following two declarations both specify private int members:

(8)

Depicting

Public

and

Private

Access

The class members are represented as smaller labeled boxes inside the class boxes.

Private members are represented as enclosed entirely within their class box.

(9)

Example of Member Access

(10)
(11)

Accessing Members from Outside the Class

To access a public instance member from outside the class, you must include the variable name and the member name, separated by a period (dot). This is called dot-syntax notation; I’ll describe it in more detail later.

For example, the second line of the following code shows an example of accessing a method from outside the class:

As an example, the following code declares two classes:DaysTempand Program.

 The two fields in DaysTemp are declared public, so they can be accessed from outside the class.

 Method Main is a member of class Program. It creates a variable and object of class

(12)

Accessing Members from Outside the Class

(13)

Restricting Access to Class Members

Simple classes define all their members as public. Consider a BankAccount program that maintains a balance data member to retain the balance in each account. Making that data member public puts everyone on the honor system.

Controlling access avoids little mistakes, such as forgetting to mark a withdrawal here or there, and manages to avoid some truly big mistakes with withdrawals.

A public example of publicBankAccount

The following BankAccount class example declares all its methods public but declares its data members, including _accountNumber and _balance, to be

private. Note that I’ve left it in an incorrect state to make a point.

The following code chunk doesn’t compile correctly yet:

// BankAccount -- Create a bank account using a double variable // to store the account balance (keep the balance in a private // variable to hide its implementation from the outside world). // Note: Until you correct it, this program fails to compile

// because Main() refers to a private member of class

(14)

BankAccount

program

using System;

namespace BankAccount {

public class Program {

public static void Main(string[] args) {

Console.WriteLine(“This program doesn‟t compile in its present state.”);

// Open a bank account.

Console.WriteLine(“Create a bank account object”); BankAccount ba = new BankAccount();

ba.InitBankAccount();

// Accessing the balance via the Deposit() method is okay --// Deposit() has access to all the data members.

ba.Deposit(10);

// Accessing the data member directly is a compile-time error.

Console.WriteLine(“Just in case you get this far the following is “ + “supposed to generate a compile error”);

ba._balance += 10;

// Wait for user to acknowledge the results.

Console.WriteLine(“Press Enter to terminate...”); Console.Read();

(15)

BankAccount

program

// BankAccount -- Define a class that represents a simple account. public class BankAccount

{

private static int _nextAccountNumber = 1000; private int _accountNumber;

// Maintain the balance as a double variable. private double _balance;

// Init -- Initialize a bank account with the next

// account id and a balance of 0. public void InitBankAccount()

{

_accountNumber = ++_nextAccountNumber; _balance = 0.0;

}

// GetBalance -- Return the current balance. public double GetBalance()

{

(16)

BankAccount

program

// AccountNumber

public int GetAccountNumber() {

return _accountNumber; }

public void SetAccountNumber(int accountNumber) {

this._accountNumber = accountNumber; // reference to current //object

}

// Deposit -- Any positive deposit is allowed. public void Deposit(double amount)

{

if (amount > 0.0) {

_balance += amount; }

(17)

BankAccount

program

// Withdraw -- You can withdraw any amount up to the

// balance; return the amount withdrawn. public double Withdraw(double withdrawal) {

if (_balance <= withdrawal) {

withdrawal = _balance; }

_balance -= withdrawal; return withdrawal;

}

// GetString -- Return the account data as a string. public string GetString()

{

string s = String.Format(“#{0} = {1:C}”, GetAccountNumber(), GetBalance());

return s; }

(18)

BankAccount

program

In this code example, _balance -= withdrawal is the same as _balance = _balance - withdrawal. (C# programmers tend to use the shortest notation

available.)

Marking a member public makes that member available to any other code within your program.

The BankAccount class provides an InitBankAccount() method to initialize the members of the class, a Deposit() method to handle deposits, and a Withdraw()

method to perform withdrawals. The Deposit() and Withdraw() methods even provide some rudimentary rules, such as “You can‟t deposit a negative number” and “You can‟t withdraw more than you have in your

(19)

BankAccount

program

However, notice that the program doesn’t build. Attempts to do so generate this error message:

„BankAccount.BankAccount._balance‟ is inaccessible due to its protection level.

The statement ba._balance += 10; is illegal because _balance isn’t accessible to

Main(), a method outside the BankAccount class. Replacing this line with

ba.Deposit(10) solves the problem. The BankAccount.Deposit() method is

publicand therefore accessible to Main() and other parts of your program.

Not declaring aclass member’s access type explicitly is the same as declaring it private.

(20)

Other levels of security

C# provides these levels of security:

A public member is accessible to any class in the program. ✦A private member is accessible only from the current class.

✦A protected member is accessible from the current class and any of its subclasses. ✦ An internal member is accessible from any class within the same program module or assembly.

A C# “module”, or assembly, is a separately compiled piece of code, either an executable program in an .EXE file or a supporting library module in a .DLL file. A single namespace can extend across multiple assemblies.

An internal protected member is accessible from the current class and any subclass, and from classes within the same module.

(21)

Why You Should Worry about Access Control

Declaring the internal members of a class public is a bad idea for at least these reasons:

With all data members public, you can’t easily determine when and how data members are being modified. Why bother building safety checks into the Deposit()

and Withdraw() methods? In fact, why even bother with these methods? Any method of any class can modify these elements at any time. If other methods can access these data members, they almost certainly will.

Your BankAccount program may execute for an hour or so before you notice that one of the accounts has a negative balance. The Withdraw() method would have ensured that this situation didn’t happen, so obviously another method accessed the balance without going through Withdraw(). Figuring out which method is responsible and under which conditions is a difficult problem.

(22)

Why You Should Worry about Access Control

Exposing internal elements leads to a distribution of the class rules. For example, my

BankAccount class doesn’t allow the balance to be negative under any circumstances. That required business rule should be isolated within the Withdraw() method. Otherwise, you have to add this check everywhere the balance is updated. Sometimes, a bank decides to change the rules so that “valued customers” are allowed to carry slightly negative balances for a short period, to avoid unintended overdrafts. Then you have to search through the program to update every section of code that accesses the balance, to ensure that the safety checks are changed.

Make your classes and methods no more accessible than necessary. Use private, if possible, and then escalate to protected, internal, internal protected, or

(23)

Accessor methods

If you look more carefully at the BankAccount class, you see a few other methods. One,

GetString(), returns a string version of the account fit for presentation to any

Console.WriteLine() for display. However, displaying the contents of a

BankAccount object may be difficult if its contents are inaccessible. The class should have the right to decide how it is displayed.

In addition, you see two “getter” methods, GetBalance() and

GetAccountNumber(), and one “setter” method, SetAccountNumber(). You may wonder why I declare a data member such as _balance private but provide a

public GetBalance()method to return its value. I have two reasons:

(24)

Accessor methods

✦ GetBalance() hides the internal format of the class from external methods.

GetBalance() may perform an extensive calculation by reading receipts, adding account charges, and accounting for any other amounts your bank may want to subtract from your balance. External methods don’t know and don’t care. Of course, you care which fees are being charged — you just can’t do anything about them, short of changing banks.

(25)

Constructors

Controlling class access is only half the problem. A class can supply an initialization method that the application calls to get things started, but the application could forget to call the method. The class starts out with garbage, and the situation gets no better after that. If you want to hold the class accountable, you have to ensure that it has a chance to start out correctly.

C# solves that problem by calling the initialization method for you — for example:

MyObject mo = new MyObject();

In other words, this statement not only grabs an object from a special memory area, but it also initializes that object’s members.

(26)

The C#-Provided Constructor

C# keeps track of whether a variable has been initialized and doesn’t allow you to use an uninitialized variable. For example, the following code chunk generates a compile-time error:

public static void Main(string[] args) {

int n; double d;

double calculatedValue = n + d; }

C# tracks the fact that the local variables n and d haven’t been assigned a value and doesn’t allow them to be used in the expression. Compiling this tiny program generates these compiler errors:

Use of unassigned local variable „n‟ Use of unassigned local variable „d‟

By comparison, C# provides a default constructor that initializes the data members of an object to

✦0 for numbers;

✦false for Booleans;

✦null for object references.

!

(27)

The C#-Provided Constructor

using System; namespace Test {

public class Program {

public static void Main(string[] args) {

// First create an object.

MyObject localObject = new MyObject();

Console.WriteLine(“localObject.n is {0}”, localObject.n); if (localObject.nextObject == null)

{

Console.WriteLine(“localObject.nextObject is null”); }

// Wait for user to acknowledge the results.

Console.WriteLine(“Press Enter to terminate...”); Console.Read();

} }

public class MyObject {

internal int n;

internal MyObject nextObject; }

(28)

The C#-Provided Constructor

This program defines a class MyObject, which contains both a simple data member n of type int and a reference to an object, nextObject (both declared internal). The

Main() method creates a MyObject and then displays the initial contents of n and

nextObject.

The output from executing the program appears this way:

When the object is created, C# executes a small piece of code that the compiler provides to initialize the object and its members. Left to their own devices, the data members

localObject.nand nextObject would contain random, garbage values.

The code that initializes values when they’re created is the default constructor. It

(29)

Replacing the Default Constructor

Although the compiler automatically initializes all instance variables to zeroes, for many classes (probably most classes),all zeroes isn’t a valid state.

To work around this problem, you can have your class provide its own explicit class constructor that C# calls automatically when the object is created. The constructor could have been named Init(), Start(), or Create(), but C# requires the constructor to carry the name of the class.

However, the way you declare and use the constructor differs:

The constructor always carries the same name as the class.The constructor can take parameters (or not).

The constructor never has a return type, not even void.

Main() doesn’t need to invoke any extra method to initialize the object when it’s created; no Init() is necessary.

(30)

Constructing something

Consider the following program, DemonstrateCustomConstructor:

// Demonstrate how you can replace the C# default constructor with your // own, custom constructor. Creates a class with a constructor and

// then steps through a few scenarios. using System;

namespace DemonstrateCustomConstructor

{ // MyObject -- Create a class with a noisy custom constructor // and an internal data object.

public class MyObject

{

// This data member is a property of the class (it’s static).

private static MyOtherObject _staticObj = new MyOtherObject();

// This data member is a property of each instance.

private MyOtherObject _dynamicObj;

// Constructor

public MyObject()

{

Console.WriteLine(“MyObject constructor starting”);

Console.WriteLine(“(Static data member constructed before “ + “this constructor)”);

Console.WriteLine(“Now create nonstatic data member dynamically:”); _dynamicObj = new MyOtherObject();

Console.WriteLine(“MyObject constructor ending”); }

(31)

Constructing something

// MyOtherObject -- This class also has a noisy constructor but // no internal members.

public class MyOtherObject

{ public MyOtherObject() { Console.WriteLine(“MyOtherObject constructing”); } }

public class Program {

public static void Main(string[] args) {

Console.WriteLine(“Main() starting”);

Console.WriteLine(“Creating a local MyObject in Main():”); MyObject localObject = new MyObject();

// Wait for user to acknowledge the results.

Console.WriteLine(“Press Enter to terminate...”); Console.Read();

(32)

Constructing something

Executing this program generates the following output:

The following steps reconstruct what just happened:

1. The program starts, and Main() outputs the initial message and announces that it’s about to create a local MyObject.

(33)

Constructing something

3. MyObject contains a static member _staticObj of class MyOtherObject. All static data members are initialized before the first MyObject() constructor runs. In this case, C# populates _staticObj with a newly created MyOtherObject before passing control to theMyObject constructor. This step accounts for the third line of output.

4. The constructor for MyObject is given control. It outputs the initial message,

MyObject constructor starting, and then notes that the static member was already constructed before the MyObject() constructor began:

(Static data member constructed before this constructor).

5. After announcing its intention with Now create nonstatic data member dynamically, the MyObject constructor creates an object of class MyOtherObject using the new operator, generating the second MyOtherObject constructing message as the MyOtherObject constructor is called.

(34)

Initializing an object directly with an initializer

Besides letting you initialize data members in a constructor, C# enables you to initialize data members directly by usinginitializers.

Thus I could have written theBankAccount class as follows:

public class BankAccount {

//Bank accounts start at 1000 and increase sequentially. private static int _nextAccountNumber = 1000;

//Maintain the account number and balance for each //object.

private int _accountNumber = ++_nextAccountNumber;

private double _balance = 0.0;

// ... other members ... }

(35)

Initializing an object directly with an initializer

Be clear about exactly what’s happening. You may think that this statement sets

_balance to 0.0 right now. However, _balance exists only as a part of an object. Thus the assignment isn’t executed until a BankAccount object is created. In fact, this assignment is executed every time an object is created.

Note that the static data member _nextAccountNumber is initialized the first time the

BankAccount class is accessed — as your tour in the debugger showed, that’s the first time you access any method or property of the object owning the static data member, including the constructor.

After the static member is initialized, it isn’t reinitialized every time you construct a BankAccountinstance. That’s different from the nonstatic members.

(36)

Seeing that construction stuff with initializers

In the DemonstrateCustomConstructor program, move the call new MyOtherObject() from the MyObject constructor to the declaration itself, as follows (see the bold text), modify the second WriteLine() statement as shown, and then rerun the program:

public class MyObject {

// This member is a property of the class (it’s static).

private static MyOtherObject _staticObj = new MyOtherObject();

// This member is a property of each instance.

private MyOtherObject _dynamicObj = new MyOtherObject(); //<-Here.

public MyObject() {

Console.WriteLine(“MyObject constructor starting”); Console.WriteLine(

“Both data members initialized before this constructor)”); // _dynamicObj construction was here, now moved up.

Console.WriteLine(“MyObject constructor ending”); }

(37)

Initializing an object without a constructor

Suppose that you have a little class to represent a Student:

public class Student {

public string Name { get; set; } public string Address { get; set; }

public double GradePointAverage { get; set; } }

A Student object has three public properties, Name, Address, and

GradePointAverage, which specify the student’s basic information. Normally, when you create a new Student object, you have to initialize its Name, Address, and

GradePointAverageproperties like this:

Student randal = new Student(); randal.Name = “Randal Sphar”;

randal.Address = “123 Elm Street, Truth or Consequences, NM 00000”;

(38)

Initializing an object without a constructor

If Student had a constructor, you could do something like this:

Student randal = new Student

(“Randal Sphar”, “123 Elm Street, Truth or Consequences, NM, 00000”, 3.51);

Sadly, however, Student lacks a constructor, other than the default one that C# supplies automatically — which takes no parameters. In C# 3.0 and later, you can simplify that initialization with something that looks suspiciously like a constructor:

Student randal = new Student { Name = “Randal Sphar”,

Address = “123 Elm Street, Truth or Consequences, NM 00000”,

GradePointAverage = 3.51 };

The last two examples are different in this respect: The first one, using a constructor, shows parentheses containing two strings and one double value separated by commas, and the second one, using the new object-initializer syntax, has instead curly braces containing three assignments separated by commas. The syntax works something like this:

new LatitudeLongitude

(39)

Initializing an object without a constructor

The new object-initializer syntax lets you assign to any accessible set properties of the

LatitudeLongitude object in a code block (the curly braces). The block is designed to initialize the object. Note that you can set only accessible properties this way, not private ones, and you can’t call any of the object’s methods or do any other work in the initializer.

The new syntax is much more concise: one statement versus three. And, it simplifies the creation of initialized objects that don’t let you do so through a constructor.

The new object-initializer syntax doesn’t gain you much of anything besides convenience, but convenience when you’re coding is high on any programmer’s list. Besides, the feature becomes essential when you read about anonymous classes.

(40)

References

Related documents

○ If BP elevated, think primary aldosteronism, Cushing’s, renal artery stenosis, ○ If BP normal, think hypomagnesemia, severe hypoK, Bartter’s, NaHCO3,

Making sacramental wine requires special attention and care, starting with qvevri washing and marani hygiene and ending with fermentation, aging and storage. During

In this study, we take ad- vantage of the Zero-Determinant (ZD) strategy to analyze the block withholding attack between any two pools, where the ZD adopter has the unilateral

When evaluating web benefit portal vendors, you should consider how quickly a client and their employees can access their data.. Some vendors can take weeks to complete the set

Such a collegiate cul- ture, like honors cultures everywhere, is best achieved by open and trusting relationships of the students with each other and the instructor, discussions

Proprietary Schools are referred to as those classified nonpublic, which sell or offer for sale mostly post- secondary instruction which leads to an occupation..

Among both those visitors who had visited a museum in San Antonio in the past 12 months or planned to do so in the next six, seeing friends or relatives as well as going to

In this PhD thesis new organic NIR materials (both π-conjugated polymers and small molecules) based on α,β-unsubstituted meso-positioning thienyl BODIPY have been