Created: 2023-08-27 10:47
Status: #concept
Subject: Programming
Tags: Object-Oriented Programming Abstraction Inheritance Polymorphism Private Property Java Locality of Behavior

Encapsulation

It is a pillar of OOP which stops other programmers from directly modifying Properties and Methods on Objects.

  • forces the user to use a Class to access and modify data via Getters & Setters.
  • this allows us to manipulate the encapsulated abstraction by itself instead of the entire system.

One good example of this is using _private variables in JavaScript Modules and using Closures to expose methods that can manipulate the _private variables of a JavaScript Object.

  • in Java, we use the private Access Modifier to only let the class use a certain attribute or method.

Encapsulating Domain Knowledge

We should split domain concepts into their own class in order to isolate changes to them and abstract their method interfaces.

  • attributes of a class that shouldn't be access outside the class or via an instance should be private.
  • this allows for less code reuse & DRY code.
  • we can easily write Unit Tests for these isolated classes.

import java.util.ArrayList;

public class WordSet {
    private ArrayList<String> words;

    public WordSet() {
        this.words = new ArrayList<>();
    }

    public void add(String word) {
        this.words.add(word);
    }

    public boolean contains(String word) {
        return this.words.contains(word);
    }
}
import java.util.Scanner;

public class UserInterface {
    private WordSet wordSet;
    private Scanner scanner;

    public userInterface(WordSet wordSet, Scanner scanner) {
        this.wordSet = wordSet;
        this.scanner = scanner;
    }

    public void start() {

        while (true) {
            System.out.print("Enter a word: ");
            String word = scanner.nextLine();

            if (this.wordSet.contains(word)) {
                break;
            }

            this.wordSet.add(word);
        }

        System.out.println("You gave the same word twice!");
    }
}

The main point here is that changes made inside the class WordSet don't affect the class UserInterface. This is because the user interface uses WordSet through the methods that it provides — these are called its public interfaces, like Getters & Setters.

Implementing Future Functionality

In the future, we might want to augment the program so that the class WordSet offers some new functionalities. If, for example, we wanted to know how many of the entered words were palindromes, we could add a method called palindromes into the program.

public void start() {

    while (true) {
        System.out.print("Enter a word: ");
        String word = scanner.nextLine();

        if (this.wordSet.contains(word)) {
            break;
        }

        this.wordSet.add(word);
    }

    System.out.println("You gave the same word twice!");
    System.out.println(this.wordSet.palindromes() + " of the words were palindromes.");
}

We simply add the new methods inside the WordSet class and use them in our code.

import java.util.ArrayList;

public class WordSet {
    private ArrayList<String> words;

    public WordSet() {
        this.words = new ArrayList<>();
    }

    public boolean contains(String word) {
        return this.words.contains(word);
    }

    public void add(String word) {
        this.words.add(word);
    }

    public int palindromes() {
        int count = 0;

        for (String word: this.words) {
            if (isPalindrome(word)) {
                count++;
            }
        }

        return count;
    }

    public boolean isPalindrome(String word) {
        int end = word.length() - 1;

        int i = 0;
        while (i < word.length() / 2) {
            // method charAt returns the character at given index
            // as a simple variable
            if(word.charAt(i) != word.charAt(end - i)) {
                return false;
            }

            i++;
        }

        return true;
    }
}

References