Logout

Home Topic 4 Last Next

--- Thinking abstractly ---

4.1.17

Identify examples of abstraction.

 

Teaching Note:

Selecting the pieces of information that are relevant to solving the problem.

LINK Thinking ahead.


 

Sample Question:

From Sample Paper 1 2014:

Refer back to the question on 4.1.14.

JSR Notes:

 

Preface. The main take-away of this section is that abstraction is the process of focusing in on one layer/component of a bigger problem, and filtering away other information, which is not necessary to juggle, or even understand, in order to work on the layer/component in question.

 

A General Definition of Abstraction:

We can think of abstraction both in terms of things and actions. And with this, it's good to think back to a general understanding of programming; programs are composed of data (attributes) and the procedures that work with that data (functions).

- With a complex system of data/things it is useful to group them into categories and/or hierarchies and/or layers. Then, through abstraction, we can focus in on the parts/layers which are useful to us, given our task at hand, and filter out/ignore the other or deeper levels of the system.

- And in terms of a complex solutions/actions, we can "decompose" bigger problems it into smaller problems and deal with each individual process on its own, disregarding the others for the time being.


So in terms of programming, we can focus in on an individual class, or a particular object of a class ("thing"), or focus in on one particular function ("action"), both of which are part of a bigger overall system. And in doing so, with abstraction we shutter away, for the time being, other classes and/or objects, and other functions/classes, simply assuming that they work, and knowing generally how they interact with the part we are working on.


A. Data Abstraction

General Example of Data Abstraction

A good example can be order made out of the complex system of people in the ISB community. We can bring order to this system both by decomposition so as to categorize, and also information hiding of details, to keep the overall understanding of the structure of the system comprehensible.

Representation of the people in the ISB community:


Programming Example of Data
Abstraction

Decomposition via an OOP Hierarchy of Classes

Representation of sale items at a Saturday Market, using an OOP class hierarchy, which via decomposition, categorizes and organizes the sale items. Information hiding encapsulates all of the details of each class within itself.

Classes:

Class Hierarchy:

The point with all this is that though it's overall a complex system, each individual class is focused in on itself, for the most part disregarding the other classes. For each individual class, how the other classes are implemented is of no concern them (with the exception of the class which it is connected to in the hierarchy).

package satrudaymarket;

public class MainForMarket {

    public static void main(String[] args) {
        Spinach[] spinaches = new Spinach[20];

        //blah blah blah
    }
}
----------------------------------------------------------------------------------------

public class SaleItem {
    private int idNumber;
    public SaleItem(){
    }
    //gets & sets and other methods
}
----------------------------------------------------------------------------------------
public class Fruit extends SaleItem{
    private double costPerKilo = 0.0;
    public Fruit(){
    }
    //gets & sets and other methods
}
----------------------------------------------------------------------------------------
public class Apple extends Fruit {
    //attributes and methods specific for an Apple
}
----------------------------------------------------------------------------------------

public class Orange extends Fruit {
    //attributes and methods specific for an Apple
}
----------------------------------------------------------------------------------------
public class Vegetable {
    //attributes and methods specific for a Vegetable
}
----------------------------------------------------------------------------------------
public class Spinach extends Vegetable{
    private String name = "not set yet";
    private boolean isStrong = false;
    public Spinach(){
    }
    public Spinach(String name, boolean isStrong){
        this.name = name;
        this.isStrong = isStrong;
    }
}


B. Procedural Abstraction

General Examples of Procedural Abstraction


Information hiding of associated procedures, in order to just understand one level/layer/part of a much more complex system/procedure. So just looking at one layer of abstraction; one procedure in and of itself, alone.


Example # 1: Making an apple pie. And just focusing in on the pie making/baking procedure itself

So in terms of filtering out unnecessary information, we don't have to worry ourselves about how the apples were grown and harvested, or how the oven works; we just need to focus on this one layer of abstraction, which is the simple steps listed above.

And, going down a level of abstraction, if our job is to cut up the apples, add the sugar, and put them in a pie shell, then we actually can abstract away the understanding of how to make the dough or collect the ingredients.


Example # 2: Organizing an IASAS Soccer Tournament

But how the schools pick their teams, the considerations coming up with the standard IASAS rules, or the certification of the officials, etc. are none of our concern; we can ignore those details, and rather focus on the tournament itself.

And going down a level of abstraction, if our job is to arrange the housing, then we don't need to concern ourselves with the selection of the officials or the running of the games.

 

Programming Example of Procedural Abstraction

public class ForAbstraction {
    static int [] grades = new int[6];
    public static void main(String[] args) {
        takeInGrades();
        averageGrade();
    }
    public static void takeInGrades(){
        Scanner snr = new Scanner(System.in);
        for(int i = 0; i < grades.length; i++){
            grades[i] = snr.nextInt();
        }
    }
    public static void averageGrade(){
        int total = 0;
        for(int i = 0; i < grades.length; i++){
            total += grades[i];
        }
        System.out.println("Average: " + total/6);
    }
}

Above, it could have been any code at all with functions. When we make function, we are employing the concept of abstraction. So, above, we can focus in on the takeInGrades( ) method, and not have to know at all the way that the averageGrades( ) method works, and vice versa. So in this way, we focus in on just what we are working on at the time, one function, in this case, at a time.

Meantime this particular class does not have to see us necessarily worrying, at least not now, about any other classes, including the System class which it uses for the println( ) method call.

 

Important Abstraction Point

Later on in the middle of 4.3, a sub-section titled "Use of Programming Languages" the curriculum makes a really important point, which is the whole gist of modular programming - programming divided up into functions, and OOP programming.

So here's the point and I'm going to make it really really big, so that we make sure we acknowledge all the important "why" of decomposition into classes/objects, and breaking programs into functions, and the then-possible use of abstraction, i.e. focusing in on one thing at a time:

Sub-programmes and objects support abstraction, which facilitates:
- ease of debugging and
- maintenance,
- reuse of code,
- modularity.

Even at this first introduction of the concept of abstraction, it is important to note these four things. I'll paste this section a couple of other places, where it will make even more sense later on. (JSR: this is the latest version, June 2021.)

Firstly, note that by "sub-programmes", we mean functions.
And by objects, we mean the things like our our Scanner object "snr", or the "out" in System.out.println( ), or something like a Student object "student1". So they are instances of various classes we either can use (like Scanner or System) or make (like Student).

Just like functions allow us to "abstract in on" them one at a time, we can do the same sort of thing with the template classes from which we make objects, in "Object Oriented Programming" (OOP). We can focus in on one function, or one OOP class at a time. And we can also use multiple copies of both functions and OOP classes.

 

- Debugging is easier because you can comment out a function and re-run your code and if the bug goes away, you know part of the problem at least must be in that function. And by the same token you can run a multi-class program, and not use one of the classes, and if the problem no longer exists, you have isolated it to be associated with that class.

- Maintenance is easier, simply because you can work with small chunks of a program rather than working with the whole thing. You can focus in on one particular task and work with it/improve it, to greater and lesser extents, in isolation from the distraction of everything else. This applies both to maintaining functions, and maintaining OOP classes from which multiple instances are made.

- Reuse of Code is facilitated simply because you don't have to write the same code over and over again, rather, in the case of functions, you just call them by name, and in the case of classes (of the "template" kind), you can simply make new instances of them.

- Modularity - what we mean here is that we have inter-changable modules. For example, you can take one AAA battery out of one device, and slip it into another; its functionality is contained, entirely, within itself. In the same way, you can take a class, or indeed a function, and "slip it into" another project or class to be used there.

And one other final note on modularity, particularly as we get into OOP programming, dividing things up into modules makes the whole system easier to understand and get a bird's eye view of.

Back to how this relates to abstraction

Whether functions or objects, and any of the four advantages above, the concept of abstraction is involved because through our -> debugging and maintenance (made easier by functions and objects/OOP approach), -> reuse of functions and/or objects, and -> the modularity offered us via both functions and objects, we are focusing in one a smaller part of a bigger problem.

 

 

FROM THIS POINT ON, IT'S A BIT BEYOND WHAT YOU NEED TO KNOW. BUT IF TIME, TAKE A LOOK


Programming Example of Procedural Abstraction
Java Abstract Classes & Methods

Java included the ability to construct abstract classes, which can be used to suggest the basic outline of a particular method that could be used in a particular OOP hierarchy.

In the example below, there will be a calculateCosts( ) abstract method, which suggests how a method for calculating costs should work. And then, there will be particular calculateCosts( ) methods implemented in their own way in different classes - each hiding its particular implementation within the code of that particular class.

Note how each calculateCosts( ) method works independently of the other, and none is concerned with the details of the other one, in the other class. Each only focuses on the information relative to it's own problem, and filters out all other information. This is classic abstraction.

And in terms of Java implementation of this, you'll note that the calculateCosts( ) abstract method in the super class AllDomesticServices has no implementation at all; it is only an idea, a suggestion of what classes extending this class should do - which leads to the ability to use abstraction by specification (see below).

(Do note that this is not at all required in IB CS, but it's one actual great example of procedural abstraction in Java.)

package runningacountry;

public abstract class AllDomesticServices {

    public AllDomesticServices(){

    }

    public abstract double calculateCosts(double [] servicesCosts);
    //An abstract method. So no implementation here. None needed here.
    //To be implemented by a class which extends this class, and 
    //adds its own particular details for how to calculate costs.
}


-------------------------------------------------------------------------------------

package runningacountry;

public class Infastructure extends AllDomesticServices{


    @Override
    public double calculateCosts(double[] roadsAndHighwaysCosts) {
        double totalInfrastructureCost = 0;
        for(int i = 0; i < roadsAndHighwaysCosts.length; i++){
            totalInfrastructureCost+= roadsAndHighwaysCosts[i];
            boolean someOtherHighwayCondition = true;
            if(someOtherHighwayCondition){
                totalInfrastructureCost+=100000;
            }
        }
        return totalInfrastructureCost;
    }
}


-------------------------------------------------------------------------------------

package runningacountry;

public class UtilityResources extends AllDomesticServices {

    @Override
    public double calculateCosts(double[] servicesCosts) {
        double totalServicesCosts = 0;
        for(int i = 0; i < servicesCosts.length; i++){
            totalServicesCosts+= servicesCosts[i];
            //And no other condition here. The point is that 
            //this calculateCosts works slightly differently than
            //the one for Infrastructure
        }
        return totalServicesCosts;
    }
}


-------------------------------------------------------------------------------------

 

Abstraction by Specification

(This is also beyond the scope of IB CS, but is good to know.)

It is good to note that since each of above procedural descriptions are fairly general - whether real life examples or programming examples - they can be applied to other similar situations. Baking a pie is basically the same approach as baking a cake. The steps to running and IASAS soccer tournament are generally the same as running an IASAS band festival. And calculate(ing)Costs( ) is similar from class to class; you take in an array of doubles, do something with it, and return a result.

So this is another advantage of abstraction. By keeping either a decomposition system fairly straight-forward, or keeping the concept of a certain procedure fairly-straight forward, we allow it to be applied in similar situations. In fact, we can simply re-use the model/procedure in slightly different contexts later one. And not only this, but within a project, or indeed within a company, there can be consistency with naming and return values/parameter lists for certain kinds of procedures and classes.