Hi, as I was told that my code doesn’t scale well at all, I thought perhaps I’d try to get a better understanding of interfaces/abstract classes and classes and the relationship between them.
I don’t want at this stage work on a big separate project as I've already got plenty to work on, rather do small exercises to help me understand the concept.
So, I was thinking, maybe I can create a small program that output the characteristics of either an employee or a client of an organisation, perhaps loop through them, just to better understand relationships between classes.
So, while I’m open to suggestions of course, here is the plan:
-create an abstract class Person, that has at least a name and a surname, perhaps a method returning a string
-create a class Employee that implements Person and that perhaps contains its own method (not sure which one at the moment, maybe something that prints "I'm a en employee")
-create a class Client that implements Person and that perhaps contains its own method (not sure which one at the moment, maybe something that prints "I'm a client")
How does that sound as a start?
OK, it's a start. Be careful with your terminology. If Person is a class then the others have to extend
it. If it's an interface the others implement
it.
Chosing between an abstract superclass and an interface is often one of those difficult decisions. Chose a class and you prevent all its subclasses from extending anything else. Chose an interface and you can't define instance variables. (There are other retrictions in what an interface can do, but Java 8 changed that significantly, eg default methods, so be certain anything you read relates to Java 8 not any earlier version.)
Right, so here is the code, it's just a simple application creating one Employee and one Client object and print out the result.
Obviously if there is anything you think isn't right, let me know and I will amend.
What do you reckon would be a good thing to do with this code to, say, enhance it? Perhaps get a list of Clients and/or Employees?
Person.java
public abstract class Person
{
private String name;
private String surname;
private int age;
abstract void printStatus();
public Person(String name, String surname, int age){
this.name = name;
this.surname = surname;
this.age = age;
}
public void setName(String name){
this.name = name;
}
public void setSurname(String surname){
this.surname = surname;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public String getSurname(){
return surname;
}
public int getAge(){
return age;
}
@Override
public String toString(){
return String.format("name: %s, surname: %s, age: %d ", getName(), getSurname(), getAge());
}
}
Employee.java
public class Employee extends Person
{
public Employee(String name, String surname, int age){
super(name,surname,age);
}
@Override
void printStatus()
{
System.out.println("I'm an Employee " + super.toString());
}
}
Client.java
public class Client extends Person
{
public Client(String name, String surname, int age){
super(name,surname,age);
}
@Override
void printStatus()
{
System.out.println("I'm a client: " + super.toString());
}
}
TestPerson.java
public class TestPerson
{
public static void main(String[] args)
{
//create objects of type person
Employee employee1 = new Employee("John","Smith",39);
employee1.printStatus();
Client client1 = new Client("Jack", "Milt", 40);
client1.printStatus();
}
}
A few observations:
-In terms of constructors, each concrete class has its own constructor which calls the parent constructor to initialize the instance variable. Is it good practice to provide an empty constructor too inside Person? I mean, really it doesn't serve any purpose because when I create an object I'm passing 3 parameters: a problem might occur if you create an object and pass no parameters, then the application won't compile, so having an empty constructor is just what, error prevention?
If a class needs some variables initialised then an empty constructor is just asking for trouble. Obviously if you don't have one then you can't call new
with no parameters, but that makes sense too. (Unless you are writing a factory class.)
Normally every subclass would override toString
, your printStatus is a bit redundant. (or just define it once in the superclass as System.out.println(this);
)
Maybe add some extra info in the subclasses - eg Employeee::salary and deal with those in the constructor appropriately?
Thanks for the feedback. To be fair the printStatus method is there only because I wanted to have to implement an abstract method, and I couldn't think of anything else :-). I usually go back to my classes after a long time and I thought it'd be nice to have an abstract method there, if I will have another method later on - and I'm sure I will - I can make that one abstract and get rid of printStatus altogether, but yes I see your point, I could call toString directly without going through printStatus.
I will add a few more properties to the class and then post back
Right, changes made. The abstract class now has a new member, salary and the old printStatus is not abstract anymore but I've implemented it in there. I've also added setters and getters for the salary
public abstract class Person
{
private String name;
private String surname;
private int age;
private double salary;
//abstract void printStatus();
public void printStatus(){
System.out.println(this);
}
public Person(String name, String surname, int age, double salary){
this.name = name;
this.surname = surname;
this.age = age;
this.salary = salary;
}
@Override
public String toString(){
return String.format("\nName: %s,\nsurname: %s,\nage: %d,\nsalary: %.2f\n ", getName(), getSurname(), getAge(), getSalary());
}
...
The two extended classes are now smaller
public class Employee extends Person
{
public Employee(String name, String surname, int age, double salary){
super(name,surname,age,salary);
System.out.print("I'm an Employee. ");
}
}
and
public class Client extends Person
{
public Client(String name, String surname, int age, double salary){
super(name,surname,age,salary);
System.out.print("I'm an Client. ");
}
}
The test class is the same.
Say I wanted to have quite a few instances of EMployees and Client, what would be the best way forward? Don't know I'm just trying to think about what it could be added to it for me to practice a little bit more with inheritance, polymorphism and relationships among classes in general
I think salary belongs in Employee - only employees have a salary. This is typical of how a subclass can extend a superclass to add more info that's just relevant to the subclass.
Override toString
in all your classes. You can use the superclass's version to avoid repetition, eg
return "Employee " + super.toString() + " salary = " + salary;
When you have multiple instances it's common to have a Collection to hold them, usually in a class that represents whatever ties those instances together. eg
class SportsClub {
...
List<Person> members = new ArrayList<>();
public void addNewMember(Person p) ...
etc
or
class Company {
...
List<Employee> employees = new ArrayList<>();
public void addNewEmployee(Employee e) ...
etc
Yes good points actually. So I've removed the salary from the parent class and added it to the employees only. Before getting to the multiple instances though, I thought it'd be nice to use composition, so perhaps I can use another class like Identification, which has an id number and a status, like permanent or contractor. This would only apply to Employees of course. After that I can implement the collection bit.
So, let's see how I can go about this. I'll create the class Identification - for the lack of a better name - and then presumably the Employee's constructor will have to take care of the initialization details, for instance, initializing the ID and the status.
In the meanwhile, here is the current amended code in its entirety:
Person.java
public abstract class Person
{
private String name;
private String surname;
private int age;
public void printStatus(){
System.out.println(this);
}
public Person(String name, String surname, int age){
this.name = name;
this.surname = surname;
this.age = age;
//this.salary = salary;
}
public void setName(String name){
this.name = name;
}
public void setSurname(String surname){
this.surname = surname;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public String getSurname(){
return surname;
}
public int getAge(){
return age;
}
@Override
public String toString(){
return String.format("\nName: %s,\nsurname: %s,\nage: %d ", getName(), getSurname(), getAge());
}
}
Person.java
public class Employee extends Person
{
private double salary;
public Employee(String name, String surname, int age, double salary){
super(name,surname,age);
//System.out.print("I'm an Employee. ");
this.salary = salary;
}
public double getSalary(){
return salary;
}
public void setSalary(double salary){
this.salary = salary;
}
@Override
public String toString(){
return "\nEmployee " + super.toString() + " \nsalary = " + getSalary();
}
}
Client.java
public class Client extends Person
{
public Client(String name, String surname, int age){
super(name,surname,age);
//System.out.print("I'm an Client. ");
}
@Override
public String toString(){
return "\nClient " + super.toString();
}
}
TestPerson.java
public class TestPerson
{
public static void main(String[] args)
{
//create objects of type person
Employee employee1 = new Employee("John","Smith", 39, 1250.50);
employee1.printStatus();
Client client1 = new Client("Jack", "Milt", 40);
client1.printStatus();
}
}
I get where you are coming from, but that extra info just looks like ordinary attributes of an employee to me, and not a good reason for another class. How about an Address class (street, postcode etc)
eh eh, too late, already implemented it : -)! I see what you mean though, every employee actually has those attributes...Might leave it there for the time being but with the intention of removing it and incorporating the members inside the Employee class.
Another thing I've done was to correctly implement toString and remove that printStatus() method because, as you correctly pointed out, it was useless. Now I print objects directly from the test class like so:
System.out.printf("%s\n %s\n %s\n",employee1,employee2,client1);
I'll implement the project class as it sounds like a good idea and a better demonstration of the has-a relationship. So what should new class have? I'm thinking a project name of course, perhaps a method that shows whether that project has been assigned, so probably a boolean method that returns true or false somehow, how about that?
You may have noticed that I has second thoughts and changed that suggestion to Address instead - it's easier to see what the attributes are. Projects are more independent, and may have multiple Employees, so it's not strictly speaking composition,.
That's OK. So one thing, if I create an Address class right, considering that both Client and Employee will use it, where would it be the best place to implement it? I was thinking that I could create an object of type Address inside Client and EMployee but that seems like a bit of a repetition. The thing is, I don't believe I can create an object in the abstract class Person though, so perhaps creating two Address objects, one for the Employee and one for the Client class is right
You create an Address class in the same package, alongside the other classes. You can then use that in any or all of the other classes. Because most people do have an address you could create a variable for that in the abstract Person class, along with any methods that may rleate to the Person's address. That way all the other classes will inherit all that.
// example of composition
class Engine {
...
}
class GearBox{
...
}
class Car {
// every Car has an Engine and a Gearbox...
Engine e;
Gearbox g;
// constructor
Car() {...
e = new Engine(...
g = new Gearbox(...
Sure yeah, will do that, but if I then want to print the address I can't do it from the abstract class because that class calls the constructor of Address, so presumably I have to implement ToString in Address and call it from there instead...I believe
EDIT: no, that's a lie, I can access the getters of Address from the abstract class because I have a reference to address, so that's fine!
Address, like pretty much every other class, needs a toString. That's a public method, so you can call it from anywhere. Going back to the car...
class car {
...
@Overide
public String toString() {
// return details of this car, delegating
// details of engine and gearbox to
// those class's toString methods
return "A car " +
" with " + e + " and " + g;
}
will return something like "A car with 6 cyclinder turbo engine and four on the floor"
OK, so would it be wrong to have the address info referenced from the Person class instead? The reason why I'm asking is because in Person I have a new Address object anyway, so I can print the address information directly from the Person class, here is the relevant code
public abstract class Person
{
private String name;
private String surname;
private int age;
private Address address;
public Person(String name, String surname, int age, int streetNumber, String streetName, String postcode){
this.name = name;
this.surname = surname;
this.age = age;
address = new Address(streetNumber,streetName,postcode);
}
...
@Override
public String toString(){
return String.format("\nName: %s,\nsurname: %s,\nage: %d, \nStreet number: %d, \nStreet name: %s, \nPostcode: %s ", getName(), getSurname(), getAge(),address.getStreetNumber(),address.getStreetName(),address.getPostcode());
}
or is it more correct to print the address info from the address class as you said in your previous reply?
If every Person has an address then Person is the right place for it.
The toString should delegate formatting an Address to the Address class as in my reply. A Person should be able to use an Address without having to know about its internals.
(Think about what happens when you add String city
to the Address class - would you remember to update Person's toString?)
There's a similar argument for the constructor - ie it should take an Address as a parameter. Taking the individual fields and creating an Address should not be a responsibilty of the Person class.
OK, makes sense for the toString, although my Address class isn't extending anything, it's just using composition. I've added a toString method in my Address class like so:
@Override
public String toString(){
return String.format("\nStreet number: %d, \nStreet name: %s, \nPostcode: %s ", getStreetNumber(), getStreetName(), getPostcode());
}
so that it knows how to print itself and then, in the Person class since I create an object of type Address somewhere I can reference that in the Person's toString like so:
@Override
public String toString(){
return String.format("\nName: %s,\nsurname: %s,\nage: %d, \naddress details: %s ", getName(), getSurname(), getAge(), address.toString());
}
Which means that I don't need to reference each field of Address like before as you suggested.
About the constructor, I understand what you mean, but how do I pass an object containing all the Address details? What I mean is,currently inside TestPerson I create objects of type Employee and Client passing the relevant address details to it
...
Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", 34, "Flinch Street","KT25AG");
Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", 44, "London Road", "SW17FG");
Client client1 = new Client("Jack", "Milt", 40, 356, "Dean Street", "SW94TG");
...
These in turn calls the relevant constructors (Employee and Client) which then call the parent constructor (Person) to initialize the members of the Address class. I appreciate that Person shouldn't be the one creating the Address, but if I don't create it there I will have to create it where, TestPerson?
cheers
I think Person is the right place, but it should take into account more possibilities:
For instance, an appartment: can a housenumber only be numerical (1, 2, 3, ...) or do you take living units into account (1, 2a, 2b, 2c, 3/101, 3/102, ... )
What when you encounter a homeless person? you should be able to have null, ...
Sir Charles Antony Richard Hoare, inventor of null
once said
I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.
Using null
to mean "not applicable" or "none" is an NPE waiting to blow up your program when you least expect it. Some better solutions are:
a valid Address instance accessible via a public static declarationAddress.NONE
that behaves appropriately.
A HomelessPerson sub-class, if there are other consquences of not having an address
or
(this is the "correct" Java 8 answer): use an Optional<Address>
since that's exactly what you mean and it's eactly what Optional
was designed for
http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
As for where to create an address..,
There is a debate to be had here, but my opinion is to prefer
Address addr = new Address (blah blah blah);
Employee e = new Employee(blah, blah, addr);
because when you change houseNumber to String you will have to change the call to the constructor(s) anyway, but like this you don't have to change Person. It's fundamental encapsulation - Person should have no knowledge of Address's internals unless it's absolutely essential to Person's functioning.
ps:
my Address class isn't extending anything
Your Address class extends Object, even though you didn't need to declare that explicitly. You needed to override the inherited toString() because the one in Object is useless to you.
OK, so not sure I've done it correctly. Here is the new logic followed by the code.
TestPerson.java - assuming that everybody has an address as said before - creates the Employees, Clients and Address objects, here the class in full:
public class TestPerson
{
public static void main(String[] args)
{
Address address = new Address(34, "Flinch Street","KT25AG");
Address address1 = new Address(44, "London Road", "SW17FG");
Address address2 = new Address(356, "Dean Street", "SW94TG");
Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", address);
Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", address1);
Client client1 = new Client("Jack", "Milt", 40, address2);
System.out.printf("%s\n %s\n %s\n",employee1,employee2,client1);
}
}
The employees and clients object contains an address as well so no need to include that inside the print statement.
Then in Employee I have the Address object passed to it which in turns gets passed to Person so it can get printed:
public Employee(String name, String surname, int age, double salary, int id, String role, Address address){
super(name,surname,age, address);
...
@Override
public String toString(){
return "\nEmployee " + super.toString() + " \nsalary = " + getSalary() + "\nID is " + identification.getID() + " \nRole is " + identification.getRole();
}
...
And Person
...
public Person(String name, String surname, int age, Address address){
...
@Override
public String toString(){
return String.format("\nName: %s,\nsurname: %s,\nage: %d, \naddress details: %s ", getName(), getSurname(), getAge(), address.toString());
}
Trouble is it's returning a NullPointerException, but surely the Address object should be initialized OK by now
Exception in thread "main" java.lang.NullPointerException
at Person.toString(Person.java:41)
at Employee.toString(Employee.java:23)
at java.util.Formatter$FormatSpecifier.printString(Formatter.java:2838)
at java.util.Formatter$FormatSpecifier.print(Formatter.java:2718)
at java.util.Formatter.format(Formatter.java:2488)
at java.io.PrintStream.format(PrintStream.java:970)
at java.io.PrintStream.printf(PrintStream.java:871)
at TestPerson.main(TestPerson.java:20)
There's not enough code there to be sure, but at a guess in Person's constructor you are failing to save the address parameter correctly into the instance variable (or maybe one of the other variables?) ?
Yes, spot on, I wasn't assigning address to anything, no wonder it didn't work : -)! Below is the full code so far. I think that I'd like to add my employees and clients objects in a List, as we discussed earlier, and perhaps sort them in alphabetical order, somthing like that, unless there is anything else that I need to change before doing so:
Person.java
public abstract class Person
{
private String name;
private String surname;
private int age;
private Address address;
public Person(String name, String surname, int age, Address address){
this.name = name;
this.surname = surname;
this.age = age;
this.address = address;
}
public void setName(String name){
this.name = name;
}
public void setSurname(String surname){
this.surname = surname;
}
public void setAge(int age){
this.age = age;
}
public String getName(){
return name;
}
public String getSurname(){
return surname;
}
public int getAge(){
return age;
}
@Override
public String toString(){
return String.format("\nName: %s,\nsurname: %s,\nage: %d, \naddress details: %s ", getName(), getSurname(), getAge(), address.toString());
}
}
Employee.java
public class Employee extends Person
{
private double salary;
private Identification identification;
//private Address address;
public Employee(String name, String surname, int age, double salary, int id, String role, Address address){
//public Employee(String name, String surname, int age, double salary, int id, String role, int streetNumber, String streetName, String postcode){
super(name,surname,age, address);
//System.out.print("I'm an Employee. ");
this.salary = salary;
identification = new Identification(id, role);
//address = new Address(streetNumber, streetName, postcode);
}
public double getSalary(){
return salary;
}
public void setSalary(double salary){
this.salary = salary;
}
@Override
public String toString(){
return "\nEmployee " + super.toString() + " \nsalary = " + getSalary() + "\nID is " + identification.getID() + " \nRole is " + identification.getRole();
}
}
Client.java
public class Client extends Person
{
public Client(String name, String surname, int age, Address address){
super(name,surname,age,address);
//System.out.print("I'm an Client. ");
}
@Override
public String toString(){
return "\nClient " + super.toString();
}
}
Address.java
public class Address
{
private int streetNumber;
private String streetName;
private String postcode;
public Address(int streetNumber, String streetName, String postcode){
this.streetNumber = streetNumber;
this.streetName = streetName;
this.postcode = postcode;
}
public int getStreetNumber(){
return streetNumber;
}
public String getStreetName(){
return streetName;
}
public String getPostcode(){
return postcode;
}
@Override
public String toString(){
return String.format("\nStreet number: %d, \nStreet name: %s, \nPostcode: %s ", getStreetNumber(), getStreetName(), getPostcode());
}
}
Identification.java
public class Identification
{
private int ID;
private String role;//permanent or contractor
public Identification(int ID, String role){
this.ID = ID;
this.role = role;
}
public int getID(){
return ID;
}
public String getRole(){
return role;
}
}
TestPerson.java
public class TestPerson
{
public static void main(String[] args)
{
//create objects of type person
Address address = new Address(34, "Flinch Street","KT25AG");
Address address1 = new Address(44, "London Road", "SW17FG");
Address address2 = new Address(356, "Dean Street", "SW94TG");
Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", address);
Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", address1);
Client client1 = new Client("Jack", "Milt", 40, address2);
System.out.printf("%s\n %s\n %s\n",employee1,employee2,client1);
}
}
Yes, that's enough for you to be able to move on to storing Collections of people.
Before coding anything, think about where you might find a collection of people in real life (eg a club or company) and if you were the secretary or personnal officer what would you need to be able to do with that collection. Write that down, the imagine it as a method signatures, then start coding.
Hi sorry I left this for so long, but other things got in the way...Anyway, in this case I would just want to print the details out, so a method like this should suffice I reckon:
printPerson(person){
// print person (employee or client)
}
So at the moment I'm creating employees and clients manually like so:
Address address = new Address(34, "Flinch Street","KT25AG");
Address address1 = new Address(44, "London Road", "SW17FG");
Address address2 = new Address(356, "Dean Street", "SW94TG");
Employee employee1 = new Employee("John","Smith", 39, 1250.50, 3456, "Permanent", address);
Employee employee2 = new Employee("Mike","Donovan", 59, 1950.50, 1456, "Contractor", address1);
Client client1 = new Client("Jack", "Milt", 40, address2);
and I would like to put them in a List - to start with. Do I actually need two Lists, one for Client and one for Employee do you reckon? Or, thinking about it, Client and Employee both inherit from Person, so I might get away by creating a list of Person and store Client and Employee in it...
Yes, you absoliutely can store any mixture of Clients and Employees in a List of Persons
Right, I've had a change of heart and refactored the whole thing, because, well, I suppose with time you tend to change your mind :-). SO here are the changes I've made.
1)moved the application into vaadin, as I needed to run it into the browser in a web form (and besides, as much as I don't like it, it's the framework used in the office, so the more I work with it the better, that's at least until they decide to go for another framweork.)
2)removed the client class, person class and some more stuff, so that now I have a webform which users can fill in with the employee's detail and ideally submit it (I'm thinking to submit it to a text file but I haven't implemented that functionality as yet, for now it is saved onto a List), and of course I got stuck with something, but let's look at the code first. So far I have:
1)the Address.java
package employee;
public class Address {
private int streetNumber;
private String streetName;
private String postcode;
public Address(int streetNumber, String streetName, String postcode){
this.streetNumber = streetNumber;
this.streetName = streetName;
this.postcode = postcode;
}
//getters
public int getStreetNumber(){
return streetNumber;
}
public String getStreetName(){
return streetName;
}
public String getPostcode(){
return postcode;
}
@Override
public String toString(){
return String.format("\nStreet number: %d, \nStreet name: %s, \nPostcode: %s", getStreetNumber(), getStreetName(), getPostcode());
}
}
2)Employee.java
package employee;
public class Employee {
private String name;
private String surname;
private Address address;
private String role;
private int ID;
private int age;
private String sex;
public Employee(String name, String surname, String sex, int age, Address address, String role, int ID){
this.name = name;
this.surname = surname;
this.sex = sex;
this.age = age;
this.address = address;
this.role = role;
this.ID = ID;
}
//getters
public String getName(){
return name;
}
public String getSurname(){
return surname;
}
public String getSex(){
return sex;
}
public int getAge(){
return age;
}
public Address getAddress(){
return address;
}
public String getRole(){
return role;
}
public int getID(){
return ID;
}
@Override
public String toString(){//needs this otherwise prints the hashcode
return String.format("\nName: %s,\nsurname: %s,\nage: %d, \nsex: %s,\naddress details: %s ", getName(), getSurname(), getAge(), getSex(), address.toString());
}
}
3)EmployeeService.java which contains the business logic: it creates an Employee object, clears the form's textFields and it's supposed to do the validation, and that's where I got a little stuck
//this class contains the business logic.
package employee;
import java.util.ArrayList;
import java.util.List;
import com.vaadin.ui.TextField;
public class EmployeeService {
List<Employee> employee = new ArrayList<Employee>();
public void printTest(){
System.out.println("Inside printTest()");
}
public void createNewEmployee(String name, String surname, String age, String sex, String role, String id, String streetNumber, String streetName, String postcode){
//System.out.println("name: " + name + "\nsurname: " + surname + "\nage: " + age + " \nsex: " + sex + "\nrole: " + role + "\nid: " + id + "\nstreetNumber: " + streetNumber + "\nstreetName: " + streetName + "\npostcode: " + postcode );
Employee theEmployee = new Employee(name, surname, sex, Integer.parseInt(age), new Address(Integer.parseInt(streetNumber), streetName, postcode), role, Integer.parseInt(id));
employee.add(theEmployee);
for(Employee currentEmployee : employee ){
System.out.println(employee);
}
System.out.println("size is " + employee.size());
}
public void clearTextFields(TextField name, TextField surname, TextField age, TextField sex, TextField role, TextField id, TextField streetNumber, TextField streetName, TextField postcode) {
// TODO Auto-generated method stub
System.out.println("Cleared!");
name.clear();
surname.clear();
age.clear();
sex.clear();
role.clear();
id.clear();
streetNumber.clear();
streetName.clear();
postcode.clear();
}
public void validateOutput(String name, String surname, String age, String sex, String role, String id, String streetNumber, String streetName, String postcode) {
// TODO Auto-generated method stub
}
}
So the thing is the validation. Ideally what should happen is that when you click the submit button:
1)if the textFields are empty, an error is shown.
2)if they're not empty then let's check that age and streetNumber are integers and if not an error is shown.
Now, the UI file, the infamous, MyUI.js has the form, instantiate the EmployeeService class and calls its methods, among which we have validateOutput():
4)MyUI.js
@Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
final TextField name = new TextField();
name.setCaption("Name:");
final TextField surname = new TextField();
surname.setCaption("Surname:");
final TextField age = new TextField();
age.setCaption("Age:");
final TextField sex = new TextField();
sex.setCaption("Sex:");
final TextField role = new TextField();
role.setCaption("Role:");
final TextField id = new TextField();
id.setCaption("ID:");
final TextField streetNumber = new TextField();
streetNumber.setCaption("Street Number:");
final TextField streetName = new TextField();
streetName.setCaption("Street Name:");
final TextField postcode = new TextField();
postcode.setCaption("Postcode:");
Button button = new Button("Submit");
button.addClickListener( e -> {
layout.addComponent(new Label("Thanks " + name.getValue()
+ ", it works!"));
EmployeeService employeeService = new EmployeeService();
employeeService.printTest();
employeeService.validateOutput(name.getValue(),surname.getValue(), age.getValue(), sex.getValue(), role.getValue(), id.getValue(), streetNumber.getValue(), streetName.getValue(), postcode.getValue());
//System.out.println("name is " + name.getValue() + surname.getValue() + age.getValue() + sex.getValue() + role.getValue() + id.getValue() + streetNumber.getValue() + streetName.getValue() + postcode.getValue());
employeeService.createNewEmployee(name.getValue(),surname.getValue(), age.getValue(), sex.getValue(), role.getValue(), id.getValue(), streetNumber.getValue(), streetName.getValue(), postcode.getValue());
employeeService.clearTextFields(name, surname, age, sex, role, id, streetNumber, streetName, postcode);
});
layout.addComponents(name, surname, age, sex, role, id, streetNumber, streetName, postcode, button);
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
}
So, would it make sense to wrap the call to validateOutput()
in a try block? I mean if the textFields are empty there is no exception, there is one (NumberFormat exception) only if I can't parse a string (value from streetNumber and age). WOuld that be the right approach?
There has always been a controversy about whether you should use Exceptions to deal with user input errors as opposed to faults in the program itself.
The Java API sometimes uses Exceptions (eg Scanner) so that makes it OK in my book.
If you don't like to do that then the alternative is to return a bool from validateOutput to show success/failure.
We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.