Lesson Weekend

Alright! We've identified our goal: creating an awesome restaurant API. We also have user stories to fuel our approach. Next, we can begin deciding what our data needs to look like to fulfill those goals.

Let's check our Trello board, and move tasks C and I into the "In Progress" column, as these user stories are being worked on by creating our data model.

Sketching our Data Model

Because we were so thorough sketching out our user's needs, determining our data model is actually a fairly easy process. It's our data and what our users want to see that should determine how we go about structuring our functionality. We can fine tune later if we realize we didn't take something into account.

Here's what I came up with:

3 classes: Restaurant, Foodtype and Review. We'll avoid creating a User class to keep things simple for now.

Time for…A new project, and...a new POJO! And another. And another.

  • Let's start a brand new app, and call call it Jadle. We'll add readme and .gitignore files right away.

  • Next, let's create our App.java, and add the dependencies we need to our build.gradle.

We won't be using handlebars, so we can safely delete that. We do need sql2o of course.

Here's what our build.gradle dependencies should look like.

build.gradle
dependencies {
  testCompile group: 'junit', name: 'junit', version: '4.12'
  compile "com.sparkjava:spark-core:2.6.0"
  compile 'org.slf4j:slf4j-simple:1.7.21'
  compile group: 'org.sql2o', name: 'sql2o', version: '1.5.4'
  compile group: 'com.h2database', name: 'h2', version: '1.4.191'
}

When you are done with that, go ahead and create a models package, and a new Restaurant.java class within models.

  • Then, we'll generate a test file for Restaurant.java with a setUp and tearDown annotation. This should of course be RestaurantTest.

Alright, so, which properties should our Restaurant object have? Here is what we settled on:

src/main/java/models/Restaurant.java
public class Restaurant {

   private String name;
   private String address;
   private String zipcode;
   private String phone;
   private String website;
   private String email;
   private int id;
}

Great. This is plenty for now.

Let's generate a constructor that takes arguments for everything except the id. We'll set that later from the database, just like we did with our To Do List.

src/main/java/models/Restaurant.java
public Restaurant(String name, String address, String zipcode, String phone, String website, String email) {
    this.name = name;
    this.address = address;
    this.zipcode = zipcode;
    this.phone = phone;
    this.website = website;
    this.email = email;
}

Constructor Overloading

But wait; what if a Restaurant we want to add doesn't have an email or website? Smaller, more old-school places may not have these things, and it seems unfair to penalize them for that.

It would be nice if we could set default text that reads "no website available" if the user doesn't supply that information. We could write logic to handle this, but let's try a different approach instead: we'll write a second constructor that accepts a different amount of arguments.

Generate a second constructor above the first that ONLY accepts arguments for name, address, zip, and phone. Our code should look now like this:

src/main/java/models/Restaurant.java
public class Restaurant {

  private String name;
  private String address;
  private String zipcode;
  private String phone;
  private String website;
  private String email;
  private int id;

   public Restaurant(String name, String address, String zipcode, String phone) {
       this.name = name;
       this.address = address;
       this.zipcode = zipcode;
       this.phone = phone;
   }

   public Restaurant(String name, String address, String zipcode, String phone, String website, String email) {
       this.name = name;
       this.address = address;
       this.zipcode = zipcode;
       this.phone = phone;
       this.website = website;
       this.email = email;
   }
}

This is pretty cool. Because Java is so strict about the amount and type of arguments supplied to a constructor, we can actually make two (or many) subtly different constructors! The amount and type of arguments a method (including a constructor) takes are called its method signature. (Remember this term! It often comes up in interview questions.)

You've likely already seen this in your own work in IntelliJ; the code completion sometimes offers us many methods that have the same name, but are all subtly different - they take different arguments, and this is why. The technical term is constructor overloading.

Note: IntelliJ is generally happier if the constructor requiring _less arguments is placed above the constructor with more arguments. This seems to work better with regards to code completion. Java itself doesn't care._

Let's set some default properties in our first constructor:

src/main/java/models/Restaurant.java
   public Restaurant(String name, String address, String zipcode, String phone) {
       this.name = name;
       this.address = address;
       this.zipcode = zipcode;
       this.phone = phone;
       this.website = "no website listed";
       this.email = "no email available";
   }

We can still set the object's properties though if we wanted to (say, if a Restaurant suddenly needed to add a website), just like we were able to set id after the fact in To Do List.

Now, if a user doesn't supply a website or email from the get go, we'll create an object using one constructor. If they do, we'll create it using another constructor. Cool!

Let's go ahead and add some tests into our RestaurantTest class to check our getters and setters. You can create them on your own, or grab them from the cheat sheet. We should feel very comfortable testing getters and setters now.

When you are done, go ahead and generate getters and setters for your Restaurant class too, as well as equals() and hashCode(). All the fields we need for our second constructor should be non-nullable.

Now that we have successfully created our Restaurant class, let's repeat this process for our Review and Foodtype class.

Based on our user stories, our data models should look like this:

src/main/java/models/Foodtype.java
public class Foodtype {
   private String name;
   private int id;
}

Generate the followowing for Foodtype:

  • a constructor
  • getters and setters,
  • equals() and hashCode()
src/main/java/models/Review.java

public class Review {

   private String writtenBy;
   private int rating;
   private int id;
   private int restaurantId; //i will be used to connect Restaurant to Review.
   private String content;
}

Generate the following for Review:

  • getters and setters
  • equals() and hashCode() (Use all properties except id to compare. Make writtenBy and content non-null.)

We're getting so good at this!

Check the cheat sheet if you are not sure what this should look like, but this is very much review at this point, and you should feel comfortable with this process.

Alright - we have plenty to work with now. Let's take a break here, and check in with our Trello board - we can now move our two tasks, 3 and 9, to the "Done" column, and then move towards implementing our other files that we need next.


Example GitHub Repo for Jadle Code at this stage

Here are our tests for our getters and setters. This code checks the alternative constructor too.

src/test/java/models/RestaurantTest.java
public class RestaurantTest {
   @Before
   public void setUp() throws Exception {
   }

   @After
   public void tearDown() throws Exception {
   }

   @Test
   public void getNameReturnsCorrectName() throws Exception {
      Restaurant testRestaurant = setupRestaurant();
      assertEquals("Fish Witch", testRestaurant.getName());
   }

   @Test
   public void getAddressReturnsCorrectAddress() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       assertEquals("214 NE Broadway", testRestaurant.getAddress());
   }

   @Test
   public void getZipReturnsCorrectZip() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       assertEquals("97232", testRestaurant.getZipcode());
   }
   @Test
   public void getPhoneReturnsCorrectPhone() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       assertEquals("503-402-9874", testRestaurant.getPhone());
   }

   @Test
   public void getWebsiteReturnsCorrectWebsite() throws Exception {
       Restaurant testRestaurant = setupAltRestaurant();
       assertEquals("no website listed", testRestaurant.getWebsite());
   }

   @Test
   public void getEmailReturnsCorrectEmail() throws Exception {
       Restaurant testRestaurant = setupAltRestaurant();
       assertEquals("no email available", testRestaurant.getEmail());
   }

   @Test
   public void setNameSetsCorrectName() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       testRestaurant.setName("Steak House");
       assertNotEquals("Fish Witch",testRestaurant.getName());
   }

   @Test
   public void setAddressSetsCorrectAddress() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       testRestaurant.setAddress("6600 NE Ainsworth");
       assertNotEquals("214 NE Broadway", testRestaurant.getAddress());
   }

   @Test
   public void setZipSetsCorrectZip() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       testRestaurant.setZipcode("78902");
       assertNotEquals("97232", testRestaurant.getZipcode());
   }
   @Test
   public void setPhoneSetsCorrectPhone() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       testRestaurant.setPhone("971-898-7878");
       assertNotEquals("503-402-9874", testRestaurant.getPhone());
   }

   @Test
   public void setWebsiteSetsCorrectWebsite() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       testRestaurant.setWebsite("http://steakhouse.com");
       assertNotEquals("http://fishwitch.com", testRestaurant.getWebsite());
   }

   @Test
   public void setEmailSetsCorrectEmail() throws Exception {
       Restaurant testRestaurant = setupRestaurant();
       testRestaurant.setEmail("[email protected]");
       assertNotEquals("[email protected]", testRestaurant.getEmail());
   }

   public Restaurant setupRestaurant (){
       return new Restaurant("Fish Witch", "214 NE Broadway", "97232", "503-402-9874", "http://fishwitch.com", "[email protected]");

   }

   public Restaurant setupAltRestaurant (){
       return new Restaurant("Fish Witch", "214 NE Broadway", "97232", "503-402-9874");

   }
}

This is what our Review class should look like:

src/main/java/models/Review.java

package models;

import java.time.LocalDateTime;

public class Review {

   private String writtenBy;
   private String content;
   private int rating;
   private int id;
   private int restaurantId;

   public Review(String writtenBy, String content,int rating,int restaurantId) {
       this.writtenBy = writtenBy;
       this.content = content;
       this.rating = rating;
       this.restaurantId = restaurantId;
   }

   //please generate your own equals and hashcode
   public String getWrittenBy() {
       return writtenBy;
   }

   public void setWrittenBy(String writtenBy) {
       this.writtenBy = writtenBy;
   }

   public int getRating() {
       return rating;
   }

   public void setRating(int rating) {
       this.rating = rating;
   }

   public int getId() {
       return id;
   }

   public void setId(int id) {
       this.id = id;
   }

   public int getRestaurantId() {
       return restaurantId;
   }

   public void setRestaurantId(int restaurantId) {
       this.restaurantId = restaurantId;
   }

   public String getContent() {
       return content;
   }

   public void setContent(String content) {
       this.content = content;
   }
}


This is our Foodtype class:

package models;


public class Foodtype {

   private String name;
   private int id;

   public Foodtype(String name) {
       this.name = name;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getId() {
       return id;
   }

   public void setId(int id) {
       this.id = id;
   }

    //please generate your own equals and hashcode
}

This is our Restaurant class:

package models;

public class Restaurant {

    private String name;
    private String address;
    private String zipcode;
    private String phone;
    private String website;
    private String email;
    private int id;

    public Restaurant(String name, String address, String zipcode, String phone) {
        this.name = name;
        this.address = address;
        this.zipcode = zipcode;
        this.phone = phone;
        this.website = "no website listed";
        this.email = "no email available";
    }

    public Restaurant(String name, String address, String zipcode, String phone, String website, String email) {
        this.name = name;
        this.address = address;
        this.zipcode = zipcode;
        this.phone = phone;
        this.website = website;
        this.email = email;
   }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getWebsite() {
        return website;
    }

    public void setWebsite(String website) {
        this.website = website;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

//generate the hashcode and equals - do not copy it, as it won't work correctly if any of your properties have changed.

}