Correct way to construct a Java console program
Asked Answered
W

2

6

I'm an old-time programmer from the 80s proficient with C and Pascal. I'm pretty new to Java and its concept. In trying to learn the new ways of Java using OOP techniques I'm rather confused about the right way of putting an application together.

I'm trying to build a basic program with the following rules.

An organisation has its own in-house library. The Staff class is general and has Name and Phone. Some of the employees is a librarian. The others are members. So Member and Librarian are specialised classes. I presume we call them inherited or extended in this context.

Now I'm trying to build basic functionality like input and print a member record. I'm trying to build a text-based menu. Perhaps later I'll consider sub-menus. For now it's just a simple menu.

I don't know if I should make the menu an object as well but that's what I think I'll do.

With this in mind, here's what I've done.

My main program:

public class Library extends Menus {
    public static void main(String[] args) {
        Menus Start = new Menus();
        Start.ShowMainMenu();

    }
}

StaffClass.java

public class StaffClass {
    private String Name;
    private String Phone;

    public void InputData() {
        Scanner UserInput = new Scanner(System.in);

        System.out.print("Enter staff name "); Name = UserInput.nextLine();
        System.out.print("Enter staff phone number "); Phone = UserInput.nextLine();

    }

    public void PrintData() {
        System.out.println("Name : " + Name);
        System.out.println("Phone : " + Phone);

    }
}//end StaffClass

Menus.java

public class Menus extends MemberClass {

    int c;
    public void ShowMainMenu() {
        Scanner ui = new Scanner(System.in);
        while(1==1) {
            System.out.println("Main menu");
            System.out.println("1. Add student");
            System.out.println("2. Display all");
            System.out.println("3. exit");
            System.out.print("Enter choice"); c = ui.nextInt();

            switch(c) {
                case 1 : getInputs(); /*System.out.println("option 1");*/ break;
                case 2 : ShowAllInfo(); break;
                case 3 : System.out.println("Leaving the program now..."); System.exit(0); break;
                default  : System.out.println("error.");
            }

        }

    }

}

MemberClass.java

public class MemberClass extends StaffClass {
    int TotalBooks;
    public void getInputs() {
        InputData();
        UpdateTotalBooks();
    }

    public void ShowAllInfo() {
        PrintData();
        System.out.println("total books taken = " + TotalBooks);
    }

    public void UpdateTotalBooks() {
        Scanner ui = new Scanner(System.in);

        System.out.print("Enter number of books "); TotalBooks = ui.nextInt();        

    }

}

It's the first Java program that I've put together with so many classes in it + a menu system.

My key question is if this is the correct way of assembling a program in Java or is there another way.

Please feel free to suggest the best options/changes to the code. But I'd like to keep the concept of generalisation and specialisation.

Thanks!

Willams answered 14/11, 2014 at 15:55 Comment(3)
as a side note: variables and members should be lowercased in Java code; only class names should be uppercase.Delight
What @ErikAllik said is correct. You'll notice that the color formatting of stackoverflow is all wrong on your code because of your capitalization. Variables, instances of classes, and methods should all be lower case (camel case works well here).Sputum
you will probably want to separate classes that read input from those that contain data or logic.Handcrafted
D
5

You are overusing classes — classes are meant to represent data types, or be holders for static methods (which are essentially just functions). You however, are using classes as a means to distribute functions and then apply bogus inheritance. Also, you're inheriting from Menus and instatiating Menus in the same class — that makes no sense whatsoever.

In other words, you don't need to use inheritance and a lot of classes just for the sake of it. There's nothing in Java other than a lot of incompetent developers who overuse classes that forces or recommends overuse of classes and inheritance.

Functions, preferrably ones with no side effects/global state, are much much easier to reason about and get right. Whereas complicated state variables scattered across a parent class chain is a shortcut to a big headache.

I would use something simple such as:

public class Library {
    public static void main(final String[] args) {
        showMainMenu();
    }

    int totalBooks;

    public static void showMainMenu() {
        final Scanner ui = new Scanner(System.in);
        while (true) {
            System.out.println("Main menu");
            System.out.println("1. Add student");
            System.out.println("2. Display all");
            System.out.println("3. exit");
            System.out.print("Enter choice");

            switch (ui.nextInt()) {
                case 1:
                    getInputs();
                    /*System.out.println("option 1");*/
                    break;
                case 2:
                    showAllInfo();
                    break;
                case 3:
                    System.out.println("Leaving the program now...");
                    System.exit(0);
                    break;
                default:
                    System.out.println("error.");
            }
        }
    }

    public static void getInputs() {
        inputData();
        updateTotalBooks();
    }

    public static void showAllInfo() {
        printData();
        System.out.println("total books taken = " + totalBooks);
    }

    public static void updateTotalBooks() {
        System.out.print("Enter number of books ");
        totalBooks = new Scanner(System.in).nextInt();
    }
}

If you need to split this up in different modules, do NOT use inheritance. Instead, create something like a State or Database class, and pass that around between static functions that either modify the passed in data or, if you want to peek into the world of functional programming and immutable data structures, return an altered copy of the data:

class State {
  // make these non-final if you want State to be mutable
  // (I'm too lazy to generate getters; and in Scala you don't have to)
  public final int totalBooks = 0;
  public final String someOtherState = "hello";

  public State(final int totalBooks, final String someOtherState) {
    this.totalBooks = totalBooks;
    this.someOtherState = someOtherState;
  }

  public State withTotalBooks(final int newValue) {
    return new State(newValue, someOtherState);
  }
  public State withSomeOtherState(final int newValue) {
    return new State(totalBooks, newValue);
  }
}

class App {
  public static void main(final String[] args) {
    State state = new State();
    // at the top-most level of the program, it's OK to use global state
    state = BookManager.updateNumBooks(state)
  }
}

class BookManager {
  public static State updateNumBooks(final State state) {
    return state.withTotalNumBooks(123);
  }

  // or, if State is mutable:
  public static void updateNumBooks(final State state) {
    // UI code here
    state.totalNumBooks = 123;
  }      
}

class HelloModule {
  public static State updateMsg(final State state) {
    // UI code
    return state.withSomeOtherState("hello world");
  }

  // or, if State is mutable:
  public static void updateMsg(final State state) {
    // UI code
    state.someOtherState = "hello world";
  }
}

// etc

And, as a final remark: all of this would look much nicer in a concise functional programming JVM language such as Scala (or even Frege). If you must stay on Java, at least read something up about Functional Programming and how you could benefit from it in the way you design your programs:

Delight answered 14/11, 2014 at 16:15 Comment(7)
Thank you Erik. I know that I'm having many classes. But doesn't my case of generalised Staff and Specialised Members demand that I have many classes? Are you suggesting that I use a single class for the whole set of Staff fields and Member-specific attributes?Willams
No, I said you can split up your functions in separate modules (represented by classes, in Java); but don't use inheritance. Your methods are just functions, so use static methods wrapped up in classes. Use classes only for things that are actually data types. In some cases, they can also represent things like factories or other containers of functions that need inheritance, but the general idea behind classes always is and will be data types.Delight
If you want a taste of what this could look like in Scala: lpaste.net/114290 — as you can see, it's obvious what modifies the state and where; but that's just one way to do it. But in any case, your data types and much of the logic will look much more readable and concise in Scala (vs Java).Delight
And prefer passing around method/function arguments! Avoid using static or instance variables to hold state that multiple functions/methods are modifying — this will lead to hell!Delight
I'm trying out Java as a means of building mobile apps. This was just an example to try out the concepts. Does Scala meet this requirement?Willams
You've suggested I use 'final' in my code. As new as I am to Java, I thought 'final' was meant for constants.Willams
yes: Scala runs on Android; it compiles to Java bytecode; and yes: your variables should be constant (i.e. immutable) by default, just like everything else. In fact, only use non-final variables (or, in Scala, vars vs vals) for top level global state, or for local isolated state for optimization.Delight
C
4

Too long for a comment, but not an answer.

  1. There will be classes. Hopefully many of them.
  2. Java naming conventions should be followed, e.g., name methods starting with a lowercase letter.
  3. StaffClass is a poor name, instead just use Staff.
  4. Consider providing input to it in a different way: as is, Staff can only be populated by its own input methods. But how data gets into a Staff instance should be completely unrelated to what a Staff actually is.
  5. There is no "One Right Way" to assemble a Java program from its classes, or even One Right Way to break up responsibilities. But the Single Responsibility Principle is important. Equally important is the notion that methods (and possibly classes) should always operate at the same level of abstraction.
Carpetbagger answered 14/11, 2014 at 16:3 Comment(6)
Yes, not an answer but definitely helpful advice. On point 4, Am I not doing what we call encapsulation by binding methods + data members of Staff in a single class?Willams
@Willams InputData does something not intrinsically related to being a Staff, though. Instead use a constructor that takes name and phone as parameters. Something else can decide where those values come from, e.g., the keyboard, a file, etc.Carpetbagger
"There will be classes. Hopefully many of them." — that's scary.Delight
@ErikAllik Not at all, classes should be focused and small, that means there will be more of them.Carpetbagger
it's a shame Java doesn't distinguish classes that are really just modules vs classes that represent data types.Delight
@ErikAllik Vs classes that are real classes, e.g., data and its operations. But meh--out of all the things that are irritating about Java I guess that one barely registers on my radar :)Carpetbagger

© 2022 - 2024 — McMap. All rights reserved.