| CS 261 | University of Puget Sound | Spring, 2020 |
|
Computer Science II
|
||
|
Abstract Data Types and their Implementations,
Some Basic Algorithms,
Object-oriented Problem Solving, and Efficiency |
||
Warning:
This course is under development.
Although the basic structure of this course is largely established,
nothing on this Web site should be considered official or even possibly
correct.
DO NOT MAKE PLANS BASED ON THE CONTENTS OF THIS SITE UNTIL JANUARY, 2020.
Historically, packages in Java provide a mechanism to group related classes together.
As Java applications have gotten larger over time, software developers have found that package permissions provide only coarse control of data and methods. If one [external] package is granted access, then all packages enjoy the same access. Java modules, new in Java Version 9, allow more detailed permissions—which package/class can directly utilize elements within another package.
Two vital components of large-scale software development involve
placing data and operations within a layered hierarchy, so that high-level capabilities can rely upon various low-level details, and
organizing data and operations into a logical structure, so that operations are placed in a unit with the data they access.
Within computer science, the first component above is called encapsulation. This principle has at least four motivations:
Pragmatically, a human is able to focus on only a limited number of details at a time. If this attention is on myriad details, then the brain has difficulty considering how those pieces might be combined conceptually, at a higher level. As an example, if a program contains 100,000 lines of code, a person will not be able to concentrate on all 100,000 lines at a time, and the assumptions of a variable used on line 72,457 may not be remembered when using that line on 23,156.
When a large program is being developed by several programmers, function naming can become a challenge. Overall application design may be able to identify the methods and capabilities that may be of use to the entire system, but helper functions often are needed as well. For those helper functions, developers must be careful that the same name is not used in conflicting ways.
For example, within an application for a restaurant, a dish class might be designed for an item on the menu (e.g., a type of food or drink), but a dish class might also be the name for various types of plates. To avoid name conflicts, different developers need to be able to identify names that are not conflicting with each other.
Organizing a large program into layers provides an effective mechanism for development and testing. At each level, capabilities can be created and carefully tested. When this layer seems appropriately robust, developers largely can rely upon this level of work, as they build the next layer on top.
As an example, when considering how to read data from a disk or CD, the actual disk is organized into rings or tracks, those rings are subdivided into sectors, and data are stored and retrieved in those sectors. Behind the scenes, the location of data on the disk must be monitored, and software is needed to keep track of the data as well as the operation of the disk. While such efforts are essential at a detailed level, most developers will rely on library functions to handle this work, and instead work at a higher level that builds upon the conceptual notion of a file.
After a layer of code has been developed and tested (e.g., for accessing disk data), application programmers must not be allowed to tinker with the underlying data or operations. That is, if a code segment is found to be correct, and if no unauthorized changes are allowed in the code or data, then the segment can still be expected to be correct.
Similarly, if a code segment turns out to be inefficient at a detailed level, programmers may want to use a different approach at a low level without changing how code at a higher level uses the implemented, low-level functions. (The interface to users may look the same, but the implementation of the interface may need to change.)
With such observations, data and operations must be encapsulated and protected, so that applications cannot violate underlying assumptions and algorithmic processing.
Within Java, a class provides a basic mechanism for bringing data and operations within a single unit. Within this framework, a class can control what data and operations may be accessed outside the class, and what data and operations are available for internal use only.
For example, a common practice specifies that data fields should always be private. This allows a class to store data in whatever way seems helpful, and the underlying representation can change over time. If one representation of data turns out to have undesired limitations, then the representation could be changed, getters and settings might be updated, but all external applications could use the low-level class as is.
By convention, Java class names start with a capital letter and designate a collection of data elements and methods.
A Java package is a mechanism for gathering several classes within a well-defined collection. For example, a library might have book classes, a catalog class, and a user class. Each of these classes has its own data and operations, and a package may clarify which data elements and operations may be used by others in the package. In this context, these classes are considered to be related to each other, and each class might begin with a statement
package library;
to designate that books, catalogs, and users may be closely connected.
Similarly, any program wishing to use a class in the library package might begin with a statement
import library;
Further, within a Java class, data fields and methods are public, private, or protected, and (as we shall discuss later in the course), these terms provide certain access within a package.
In contrast, the terms public, private, and protected generally provide somewhat more restricted classes outside outside a package.
Overall, for many versions of Java (up through version 8), the distinction of classes within a package versus classes outside a package allowed reasonable control over what class could access what material.
However, as software applications have increased in size, programmers realized a need for finer control for access to data and/or methods. Control within a package could be specified reasonably, but different packages should be given different levels of access. Further, the management of large programs could be aided by explicitly specifying what classes or packages were required at each stage and by identifying what classes or packages were allowed to use what.
Starting with Java, version 9, the concept of a module was introduced to provide this added level of access control. In particular,
a module specifies a collection of packages and classes, and includes a file, module-info.java, which has an overall structure
module moduleName {
requires myFavoritePackage;
}
where a requires statement is used to identify each external module needed for the current code.
In addition, the module-info.java grants permission to other modules to utilize the current module:
module moduleName {
exports myPackage
}
In this format, we grant permission to any module to use the myPackage class. Additional rules for access of packages within modules provide more control, but such details are left until later.
Although modules provide considerable control over what packages and classes can be utilized where, simple programs (e.g., those at the start of this course) likely will involve only a few classes, located within a single package, which in turn is placed within a single module. In this situation, module-info.java files still should be present (e.g., when defining a project in the eclipse programming environment), but this module-info.java file may be an empty shell:
module examples {
}
This current reading is intended to provide a general overview of the notion of packages and modules in Java. An interested reader might search the Web for additional information, such as Java 9 Modules (Part 1): Introduction by DZone.
|
created 7 September 2019 by Henry M. Walker last revised 11 September 2019 |
|
| For more information, please contact Henry M. Walker at walker@cs.grinnell.edu. |