Java: OOP Encapsulation vs No encapsulation? - java

Hello I did a Binary Tree program for my Java Class and I got lots of points off for "No OOP Encapsulation" .. well I thought encapsulation was just using classes? What should I done different to enforce OOP Encapsulation? Below was my working code . Thanks for any input or advice
public class BinTree {
private Node root;
private static class Node{
Node left;
Node right;
int value;
Node(int newValue){
left = null;
right = null;
value = newValue;
}
}
BinTree(){
root = null;
}
public void insertNode(int value){
root = insertNode(root, value);
}
private Node insertNode(Node node, int value){
if (node==null){
node = new Node(value);
}
else{
if (value <= node.value){
node.left = insertNode(node.left, value);
}
else {
node.right = insertNode(node.right, value);
}
}
return(node);
}
public void treeWalk(){
treeWalk(root);
}
private void treeWalk(Node node){
if(node != null){
treeWalk(node.left);
System.out.println(node.value);
treeWalk(node.right);
}
}
}

#Marco13 is probably correct, though the only way to be sure what the marker meant is to actually ask him or her.
For what it is worth, I think your marker is incorrect here. Clearly, the outer class is properly encapsulated, and the nested class is private which means it is not visible outside of the outer class encapsulation boundary.
It is accepted Java practice that private inner classes do not necessarily need getters and setters for their fields.
Encapsulation is not a religion. It is used in Java for a purpose; i.e. to prevent implementation details from leaking. And declaring getters and setters also facilitates subclassing. But when the supposed purposes are moot (as they are here) use of encapsulation is not necessary.
If you want an apposite example, take a look at the Java source code for the standard LinkedList class:
http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/LinkedList.java#LinkedList.Entry
Note that the Entry class does not have getters or setters ... and its fields are not declared as final either.

When this was an assignment, the term "No OOP Encapsulation" probably refers to the fact that you did not use setters and getters for the fields of the Node class. So you should make left, right and value private, and offer methods for reading/writing the values of these fields accordingly.
(Note that "encapsulation" in this case also means that you do not have to create a method for modifying the value: This is only set in the constructor, and may not be changed later. So in fact, the value may (and should) even be private final.)

Wikipedia writes:
Under this definition, encapsulation means that the internal representation of an object is generally hidden from view outside of the object's definition. Typically, only the object's own methods can directly inspect or manipulate its fields.
In your case, the fields of the Node class are accessed from methods of class BinTree. Therefore, the Node is not encapsulated from the BinTree.
Your teacher might have wanted you to move the methods the access the node's state into the node class:
class BinTree {
private Node root;
private static class Node {
Node left;
Node right;
int value;
Node(int newValue) {
left = null;
right = null;
value = newValue;
}
void insert(int newValue) {
if (newValue <= value) {
if (left == null) {
left = new Node(newValue);
} else {
left.insert(newValue);
}
} else {
if (right == null) {
right = new Node(newValue);
} else {
right.insert(newValue);
}
}
}
void walk() {
if (left != null) {
left.walk();
}
System.out.println(value);
if (right != null) {
right.walk();
}
}
}
BinTree() {
root = null;
}
public void insertNode(int value) {
if (root == null) {
root = new Node(value);
} else {
root.insert(value);
}
}
public void treeWalk() {
if (root != null) {
root.walk();
}
}
}
Now, whether this is better design is somewhat questionable. Sure, the object oriented purist rejoices at the shorter parameter lists, and that all methods only access the fields of this, but a pragmatic programmer will find the duplicated null checks just as bad, or possibly worse than, the lack of strict encapsulation in your approach.
Personally, I consider encapsulation (or more generally information hiding) an indispensable technique for writing all but the most trivial of programs. But I disagree that the proper size of a capsule need always be a single object. In your original code, the capsule surrounds the entire tree, and that's good enough for me.

The idea of OOP is to make code more reusable and more object like, when you are creating an object you want to be able to use all the functions of that object, this also includes accessing data. The problem comes down to creating more than 1 type of object and accidentally overriding the value of data. This is why you encapsulate.
here is an example of encapsulation
private int health;
public void getHealth(){
return health;
}
public int setHealth(int h){
this.health = h;
}
you ask why bother having 2 methods for this property? when you instantiate the class, you don't want to directly access its property but do it indirectly to avoid overriding its default value. You set the property to private so that no class can call it directly.

Related

Compilation error while implementing the Depth first search Recursively

I am new to using recursion for my methods. I tend to steer away from them for quite a few reasons. However, for a project, it seems to easier to have a recursive method instead of a looping one since I am trying to do Depth First Traversal for a Graph.
Since I am not too well versed in recursion, I don't understand why I am getting the following error.
This method must return a result of type LinkedList.Node
The code I have currently is:
public Node DFSTime(Node node){
if(node == null){
System.out.println("No path found");
return null;
}
else if(node.element.equals(destinationAirport)){
return node;
}
else{
stack.push(node);
DFSTime(node.nextNode);
}
}
It is unfinished code since I still need to implement some logic, however, I don't understand how to eliminate the error. Is there something very basic that I am missing?
The reason of the compilation error is pretty trivial. The compiler clearly tells that didn't provide the result to return for all possible cases.
The more important is that your current approach is not correct.
it seems to easier to have a recursive method instead of a looping one since I am trying to do Depth First Traversal for a Graph
There are crucial things to consider:
Field nextNode is very suspicious. If each Node holds a reference pointing to a single node only, in fact the data structure you've created by definition isn't a Graph, but a Singly linked list. And doesn't make sense to implement DFS for a list. Every node should point to a collection of nodes, no to a single node.
You have to distinguish somehow between unvisited nodes and nodes that are already visited. Or else you might and up with infinite recursion. For that, you can define a boolean field isVisited inside the Node, or place every visited node into a HashSet.
Since you've chosen to create a recursive implementation of DFS, you don't need to create a stack. It's required only for iterative implementation.
Don't overuse global variables. I guess you might want to be able to check whether it is possible to reach different airports of destination without reinstantiating the graph.
Use getters and setters instead of accessing fields directly. It's a preferred practice in Java.
Your method might look like this (it's not necessary that element should be of type String it's just an illustration of the overall idea):
public Node DFSTime(Node node, String destinationAirport){
if(node == null || node.isVisited()) {
return null;
}
if (node.getElement().equals(destinationAirport)) {
System.out.println("The destination airport was found");
return node;
}
node.setVisited(true);
for (Node neighbour: node.getNeighbours()) {
Node result = DFSTime(neighbour, destinationAirport);
if (result != null) return result;
}
return null;
}
And the node might look like this:
public class Node {
private String element;
private List<Node> neighbours;
private boolean isVisited;
public Node(String element, List<Node> neighbours) {
this.element = element;
this.neighbours = neighbours;
}
public void setVisited(boolean visited) {
isVisited = visited;
}
public boolean isVisited() {
return isVisited;
}
public void addNeighbours(Node neighbour) {
neighbours.add(neighbour);
}
public String getElement() {
return element;
}
public List<Node> getNeighbours() {
return neighbours;
}
}
You should have a default return statement at the end of the function after the closing of the else.
In methods conditional blocks (if-else), you need to make sure you are returning appropriate Type from all conditional statements, so that there is no compile-time error. In your case, else block is recursively calling DFSTime(..) without returning anything.
You might want to return reference which gets called via recursive call, something like below:
public Node DFSTime(Node node){
if(node == null){
System.out.println("No path found");
return null;
}
else if(node.element.equals(destinationAirport)){
return node;
}
else{
stack.push(node);
Node node = DFSTime(node.nextNode);
return node;
}
}

A* pathfinding with Multiple Agents

I've currently been learning and programming pathfinding(in Java) using the A* algorithm. A problem I've run into is when multiple entities are trying to pathfind, they both alter the previousNode(the Node that the Node being calculated on came from), messing up the algorithm, and eventually Node A will point to Node B and Node B will point to Node A.
How can I change the algorithm to either
Not use this previousNode system that is littered throughout all of the A * algorithms(that I have seen, that is)
Alter this system to be used concurrently
I am trying to avoid having one entity finish pathfinding, then telling the next entity to pathfinding, and so on. Like doing a wait() - notify() pair in Java.
public Path findPath(int startX, int startY, int goalX, int goalY) {
//Path is basically just a class that contains an ArrayList,
//containing Nodes, which contains the steps to reach a goal.
if(map.getNode(goalX, goalY).isObstacle()) {
return null;
}
map.getNode(startX, startY).setDistanceFromStart(0);
closedList.clear();
openList.clear(); //A List with added getFirst() - gets the first Node in the list
openList.add(map.getNode(startX, startY));
while(openList.size() != 0) {
//Node contains a List that has all of the Nodes around this node, a
//F, G, and H value, and its row(y) and column(x)
Node current = openList.getFirst();
if(current.getX() == goalX && current.getY() == goalY) {
return backtrackPath(current);
}
openList.remove(current);
closedList.add(current);
for(Node neighbor : current.getNeighborList()) {
boolean neighborIsBetter;
//If I've already searched this neighbor/node, don't check it
if(closedList.contains(neighbor)) {
continue;
}
if(!neighbor.isObstacle()) {
float neighborDistanceFromStart = (current.getDistanceFromStart() + map.getDistanceBetween(current, neighbor));
if(!openList.contains(neighbor)) {
openList.add(neighbor);
neighborIsBetter = true;
} else if(neighborDistanceFromStart < current.getDistanceFromStart()) {
neighborIsBetter = true;
} else {
neighborIsBetter = false;
}
if(neighborIsBetter) {
neighbor.setPreviousNode(current);
neighbor.setDistanceFromStart(neighborDistanceFromStart);
neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY));
}
}
}
}
return null;
}
public Path backtrackPath(Node fromNode) {
Path path = new Path();
while(fromNode.getPreviousNode() != null) {
path.prependWaypoint(fromNode);
fromNode = fromNode.getPreviousNode();
}
return path;
}
I am specifically talking about(within findPath())
if(neighborIsBetter) {
neighbor.setPreviousNode(current); //previousNode is a value in the Node class that points to the Node that it came from
neighbor.setDistanceFromStart(neighborDistanceFromStart);
neighbor.setHeuristic(getManhattanDistance(neighbor.getX(), neighbor.getY(), goalX, goalY));
}
I don't think you can do A* (or any pathfinding algorithm, for that matter) without somehow storing a backpointer for a given path. So that leaves you with two options
Require each agent (Thread, I assume) to create their own copy of the graph to work on. That way each A* call going on won't interfere with one another, as they are working with the fields of the same node on different graphs.
Change your A* code to be able to handle multiple concurrent calls.
Option 1 is fairly self-explanatory and probably the better option. If this is just for you, you should probably just go with that one (instead of trying to make A* fully concurrent on a single graph). This would entail adding map as an input parameter (and requiring that concurrent calls should use a different map instance, either throwing an exception or having unspecified behavior if that doesn't occur). Additionally, you should instantiate closedList and openList as new data structures in each call, rather than share a list.
If that's not to your liking - you really want to fully encapsulate the mutli-call usage into the method itself, I think the simplest way you could do this is require an additional parameter of an id - some unique string that is guaranteed not to be the same as the id of another concurrent call. So the header of A* now looks like:
public Path findPath(final String ID, int startX, int startY, int goalX, int goalY) {
From there, change all of the implementations of each of the settable pathfinding fields in Node to a HashMap with the id as the key. From your code, I'm going to guess that your Node class looks something like this:
public class Node{
//Fields used by the A* call - no problem here
private boolean obstacle;
//Fields *edited* by the A* call
private float distanceFromStart;
private Node previous;
private int heuristic;
//other fields and stuff
public boolean isObstacle(){
return obstacle;
}
public float getDistanceFromStart(){
return distanceFromStart;
}
public void setDistanceFromStart(float f){
distanceFromStart = f;
}
public Node getPrevious(){
return previous;
}
public void setPrevious(Node p){
previous = p;
}
public int getHeuristic(){
return heuristic;
}
public void setHeuristic(int h){
heuristic = h;
}
}
We can edit the edited fields to be able to store many values, by id, as such:
public class Node{
//Fields used by the A* call - no problem here
private boolean obstacle;
//Fields *edited* by the A* call
private HashMap<String,Float> distanceFromStart;
private HashMap<String,Node> previous;
private HashMap<String,Integer> heuristic;
//other fields and stuff
public boolean isObstacle(){
return obstacle;
}
public float getDistanceFromStart(String id){
return distanceFromStart.get(id);
}
public void setDistanceFromStart(String id, float f){
distanceFromStart.put(id, f);
}
public Node getPrevious(String id){
return previous.get(id);
}
public void setPrevious(String id, Node p){
previous.put(id,p);
}
public int getHeuristic(String id){
return heuristic.get(id);
}
public void setHeuristic(String id,int h){
heuristic.put(id,h);
}
}
From there, simply edit your A* method to give the id from the method call to the getters and setters when called for. So long as two concurrent method calls don't have the same id value, they won't interfere with each other. Three things to keep in mind for this to work correctly:
Make sure that every editable field gets this treatment. It won't work if you forget about one. Non-editable fields (fields that don't get altered as a byproduct of running A*) can stay singular.
If you use the the above, you should probably add to the cleanup stage a step of removing all the information for the given ID from the graph, or the nodes' hashmaps will grow larger with each call.
Either way, you still should make openList and closedList new local instances, no matter what concurrent approach you pick. There's nothing to gain from making openList and closedList shared instances, and only bugs can come of it.
List<Node> closedList = new LinkedList<Node>();
List<Node> openList = new LinkedList<Node>();
//Don't have to clear them anymore - they're new lists
openList.add(map.getNode(startX, startY));

How to use Comparable as a Generic parameter in a Class

I have a home work in a data structures course, the question is:
Implementation of doubly-linked list class.
the methods:
display()
length() or size()
insertSorted(Comparable)
insertToEnd(Comparable)
insertToHead(Comparable)
delete(Comparable)
boolean search(Comparable)
You must do this in JAVA
Create an application layer to test your class and its methods.
Compress all of your source files into a file and rename it as CS214HW1_first_lastName.zip Put your name in the filename. If needed, add a ReadMe.txt file for extra information such as compilation.
I implemented everything correctly and the code is working fine, but I used for example: insertSorted(int) instead of insertSorted(Comparable), because I didn't know how to do it.
I searched online, and read the JAVA documentation for (Comparable) but it is not enough :(
Can anybody help, please it is very important?
Here's some of my code, I can't write it all, cuz I don't want my friends to get the same code.
I will take zero if there is same code.
Code:
class DLL {
class Node {
Node next;
Node prev;
int data;
Node() {
next = null;
prev = null;
data = 0;
}
Node(int dt) {
next = null;
prev = null;
data = dt;
}
}
Node head;
void insertToHead(int dt) {
if (head == null) {
head = new Node(dt);
}
else {
head.prev = new Node(dt);
head.prev.next = head;
head = head.prev;
}
}
public static void main(String args[]) {
DLL dll = new DLL();
dll.insertToHead(1);
dll.insertToHead(2);
dll.insertToHead(3);
}
}
Please, somebody, tell me what to change in the beginning of the class.
are we gone use extends or implements Comparable<E> or what!
and what changes should i do the method insertToHead(Comparable)
what changes should i do to the main.
You would probably like to look into how generics work as well. The basic idea is that you would like to set up your class so that it will not know exactly the specific type of object but can be given some hint at the types of things it can expect of a declared generic type.
In your case, you would like to set up your list so that you can create linked lists of anything that can be compared. Java has a class for that which you have mention called Comparable<E> this tells Java that it will be able to call such methods as compareTo on the provided object.
More specifically to your closing questions:
Use the following style of class declaration MyClass<MyGenericType extends Comparable<MyGenericType>>. In your case DLL<E extends Comparable<E>>.
Switch the method arguments to accept E our declared generic type.
You should use the class Integer instead of the primitive type int, and change the creation of your list to DLL<Integer> dll = new DLL<Integer>().
Fully updated version of provided code:
public class DLL<E extends Comparable<E>> {
class Node {
Node next;
Node prev;
E data;
Node() {
next = null;
prev = null;
data = null;
}
Node(E dt) {
next = null;
prev = null;
data = dt;
}
}
Node head;
void insertToHead(E dt) {
if (head == null) {
head = new Node(dt);
}
else {
head.prev = new Node(dt);
head.prev.next = head;
head = head.prev;
}
}
public static void main(String args[]) {
DLL<Integer> dll = new DLL<Integer>();
dll.insertToHead(1);
dll.insertToHead(2);
dll.insertToHead(3);
}
}
This new implementation should provide a hint for how to proceed with some of the other homework tasks. For instance you can now compare objects just by their compareTo method which might useful for sorting hint hint.
That doc page gives a very good explanation for how to use this method. You should note that in their docs, they use a generic type called T instead of E, it really doesnt make a difference you can call it whatever you want provided it is unique to your program.
Edit:
An each hint in the sorting direction:
Ojbects which extend the Comparable class have a method which is called compareTo this method is set up so you can call:
object1.compareTo(object2);
this method returns an int which will be:
> 0 when object1 is greater than object2
= 0 when object1 is equal to object2
< 0 when object1 is less than object2
I don't want to give away too much as this is a homework assignment but here is my hint:
The way the above code sets up your classes, you would be able to tell the relationship between NodeA and NodeB by calling:
NodeA.data.compareTo(NodeB.data)
this will return an integer which gives your information according to the list above.
The <=,>=,== operators are likely found in the Integer class's compareTo method.
Something like:
public int compareTo(Object o) {
int otherNumber = ((Integer) o).intValue();
int thisNumber = this.intValue();
if (otherNumber > thisNumber) {
return 1;
} else if (otherNumber < thisNumber) {
return -1;
} else {
return 0;
}
}
but more likely they just do something like:
public int compareTo(Object o) {
return this.intValue() - o.intValue(); // possibly normalized to 1, -1, 0
}
See the Docs on Integer for more info on this.

Two objects refer to each other with final references

I have a class that looks something like this:
public class Node {
private final Node otherNode;
public Node(Node otherNode) {
this.otherNode = otherNode;
}
}
and want to do something like
Node n1, n2 ;
n1 = new Node(n2);
n2 = new Node(n1);
but obviously cannot since n2 is not initialized yet. I don't want to use a setter to set otherNode because it's final, and thus should only be set once ever. What is the cleanest approach to accomplishing this? Is there some fancy Java syntax I'm unfamiliar with to let me do this? Should I use an initialize method in addition to the constructor (ugly), or just cave and use a setter (also ugly)?
Have a second constructor that takes no parameters and constructs its own Node, passing itself as the other's "other".
public class Node
{
private final Node otherNode;
public Node(Node other)
{
otherNode = other;
}
public Node()
{
otherNode = new Node(this);
}
public Node getOther()
{
return otherNode;
}
}
Then when using it:
Node n1 = new Node();
Node n2 = n1.getOther();
Assuring that they refer to each other:
System.out.println(n1 == n1.getOther().getOther());
System.out.println(n2 == n2.getOther().getOther());
System.out.println(n1 == n2.getOther());
System.out.println(n2 == n1.getOther());
These all print true.
(This is a supplement to rgettman's answer.)
A more general solution is to write a constructor like:
private Node(final int numNodesInLoop) {
if(numNodesInLoop < 1) {
throw new IllegalArgumentException();
}
Node head = this;
for(int i = 1; i < numNodesInLoop) {
head = new Node(head);
}
this.otherNode = head;
}
Your case, with two nodes, would be instantiated as new Node(2).
I made the above private, per a comment by user949300 to rgettman's answer, because the meaning of a Node constructor that takes an int is not very guessable (it creates a loop?!), so it's better to wrap it in a static factory method whose name makes its functionality clear:
public static Node newNodeLoop(final int numNodes) {
return new Node(numNodes);
}
(This is also more future-proof in case you later have a need for another constructor that would take an int, for whatever reason. You can then modify this constructor to take a dummy argument as well, just enough to tell the compiler what constructor you want. The factory method would still have the same contract.)

Java Pass by value, Advantage or Disadvantage?

Java is pass by value. What if I need a pass by reference. For example in the following code I need a pass by reference mechanism.
public class BinaryTree {
public TreeNode root;
public BinaryTree(){
root = null;
}
public TreeNode insert(TreeNode temp,int x){
if(temp == null){
temp = new TreeNode();
temp.key = x;
temp.left = temp.right = null;
return temp;
}
if(temp.key > x)
temp.left = insert(temp.left,x);
else if(temp.key < x)
temp.right = insert(temp.right,x);
return null;
}
}
When insert is called with root, I need root to be passed as a reference so as to change its value. But this does not happen in Java, since it's pass by value. In C/C++ the above could be easily achieved. Don't you think that this is a drawback of Java? How could such problems be solved in Java?
In Java, if you have a reference type the reference is passed by value.
Inside the method you can mutate the object that was passed and the caller will see those changes.
Don't you think that this is a drawback of Java?
No. Because:
There are few cases where you really need it.
There are workarounds (see below).
Implementing pass by reference in Java would be difficult. It makes code generation and garbage collection significantly more complicated.
(OK ... so really these are counter-arguments. But we are talking about a language design issue here, and any rational discussion of language designs has to weigh up the pros and cons of supporting a particular feature. And that includes implementation cost and performance issues.)
How could such problems be solved in Java?
The general approach is to restructure your code so that the variable you need to update in the called method is replaced with a reference to a mutable object or an array. This may entail the caller doing a bit more work, but that is generally acceptable.
Alternatively (and in your example) restructure the code so that call by reference is unnecessary.
In your example, there are two observations to make:
The "call-by-reference" mechanism is only used in the case where the tree is empty. It is not difficult to change this so that it is not necessary.
In fact, your use of call-by-reference, and in fact the entire insert method, is a leaky abstraction. There is nothing to stop you calling the method with a node object that is nothing to do with the current BinaryTree instance. You are relying on the caller to maintain the (implied) invariants of the tree.
The following version addresses both of these issues:
public class BinaryTree {
private static class TreeNode { ... }
public TreeNode root;
public BinaryTree(){
root = null;
}
public void insert(int x) {
root = insert(root, x);
}
private TreeNode insert (TreeNode node, int x) {
if (node == null) {
return new TreeNode(x);
}
if (node.key > x)
node.left = insert(node.left, x);
else if (node.key < x)
node.right = insert(node.right, x);
return node;
}
}
(I don't exactly like the way that we reassign the left / right pointers at each level after the insertion, but it does make the insertion logic simple.)
root can be changed by getting the return value.
public void insert(int x) {
root = insert(root, x);
}
I changed the method insert(...) a little.
private TreeNode insert(TreeNode temp,int x){
if(temp == null){
temp = new TreeNode();
temp.key = x;
temp.left = temp.right = null;
}
if(temp.key > x)
temp.left = insert(temp.left,x);
else if(temp.key < x)
temp.right = insert(temp.right,x);
return temp;
}
Java is pass by value for everything. Whether you are working with primitives or with reference types.
The "value" or a reference type is the reference itself, so when using reference types the reference itself is passed.

Categories

Resources