Where to add members of java classes - java

Implement a class Student. For the purpose of this exercise, a student has a name and a total quiz score. Supply an appropriate constructor and methods getName(), addQuiz(int score), getTotalScore(), and getAverageScore(). To compute the latter, you also need to store the number of quizzes that the student took.
...
I am having an especially hard time with the scores and name. Do I add the scores to both the Student.java and the StudentTester.java files or only the tester? I cannot figure this out.
Here is my code:
/** A student has taken a number of quizzes and has an average score
based on the quizzes that were taken.
*/
public class Student
{
private String name;
private double totalscore;
private int numquiz;
}
// Constructs a student object with the name "MccStudent" and with zero total of quiz scores
public Student(String "mccStudent")
{
this.name = studentname;
numquiz = 0;
totalscore = 0;
}
public String getName()
{
return name;
}
// Adds the number of quizzes taken
public void addQuiz(double quizscore)
{
totalscore+=quizscore;
numquiz++;
}
// Returns the total quiz score
public double getTotalScore ()
{
return totalscore;
}
// Returns the avaerage grade
public double getAverageScore ()
{
return totalscore/numquiz;
}
}​
/** Create a class to test the Student class.
*/
public class StudentTester
{
/**
Tests the methods of the Student class.
*/
public static void main(String[] args)
{
// Create an object
Student mccStudent = new Student();
mccStudent.addQuiz(100);
mccStudent.addQuiz(80);
mccStudent.addQuiz(95);
mccStudent.addQuiz(97);
System.out.println(mccStudent.getName());
System.out.println(mccStudent.getTotalScore());
// Display average quiz score
System.out.println(mccStudent.getAverage.Score());
}
}​

First of all try to understand what Constructor is. Here is oracle documentation with nice examples: Constructor. Ill write simple example for you. Student is new Object that has String name attribute.
public class Student {
public String name; //name of student
public Student(String name) {//Constructor for student, receiving name when u create new object Student
this.name = name; //set received name to this public String name
}
/**
* When u call this method you will get inputed name from constructor
* so if u call Student stud = new Student("John");
* new Student("John") is constructor!
* with stud.getName(); you will get "John".
* This is called getter.
* #return name of student
*/
public String getName() {
return name;
}
}

There are some major issues here, for one the first brace is closed after declaring your instance variables, so the rest of the code for Student is out of scope. Remove the first close brace, and that should help that.
Another issue is the way you are using your constructor (the part that says public Student(String "mccstudents")). You need to providing a variable name there, and then whenever you make a new object, you pass in a string, and that will take the place of the variable name.
Not to sound like one of your teaches, you really shouldn't leave this to last minute To jump the gun on any number of people who will write a reply for this, this site is designed to help specific issues, not analyse the whole program.
Are there particular issues and concepts that you are not understanding that I can help with?

You add the fields to the class itself (Student.java).
Only code that performs tests should appear in the test class (StudentTester.java).

Related

How to use an object without knowing the objects' name

I'm not too sure how to word this so it makes sense, but I'll try my best.
Say I have 2 classes. My main class, and a Person class.
My main class will create some Objects from the Person class like this
public class Example {
static Person bob = new Person(23);//Age
static Person fred = new Person(34);
static Person John = new Person(28);
//..and so on
public static void main(String args[]){
..
}
}
and in my Person class..
public class Person{
private int age;
public Person(int age){
this.age = age;
}
public int getAge(){
return this.age;
}
}
Now, if I wanted the age of fred, I'd just call Fred.getAge();.
But, in my program, I don't know what person I'm getting the age of. It randomly selects one, and I need to get the name without directly calling the object. For example, I would have something like this in my Person class:
public static Object getPerson(){
//Some code to get a random integer value and store it it Var
switch(Var){
case 1:
return bob;
case 2:
return fred;
case 3:
return john;
}
}
What I would expect this to do is return an Object that I could then use like this:
public static void main(String args[]){
System.out.println(Person.getPerson().getAge());
}
What I thought that would have done was first call getPerson() which randomly returns either bob, fred, or john, and then it would call getAge(). So if getPerson() returned fred then it would be the same as doing fred.getAge();
Now, this doesnt work, and this was the only way I thought of that made sense to me.
How do I do this so it actually does what I want?
I'm very new to Java, and OOP, and this is my first time really working with different Objects. So I'm sorry if I'm using the wrong terms and explaining things weirdly.
Change
public static Object getPerson(){
to
public static Person getPerson(){
You can't call getAge on an Object, because the Object type does not have getAge() defined.
Why not put the name as a property of the Person class?
class Person {
// ... your existing code for age...
private String name;
String getName() { return name; }
// add name to constructor...
public Person(String name, int age) {
// set them up here...
}
}
The way I see it, is that name is for you as a human, but variables john are irrelivant to the program and computer.... you can even use p1 = Person("Joe", 42);
To get a person by age, you can use a Map with age as key, and person as value.
It could be the case that this is a misunderstanding, but how I'm interpreting the issue is as follows:
You need a (better) place to store all of your Person objects instead of having them as static variables.
You need a way to randomly select from wherever you're storing those objects.
Let's address the main issue first. You're creating these as static variables when they probably shouldn't be; they should just be created as entries into an array.
The way to do this is through this declaration:
Person[] people = new Person[] {new Person(23), new Person(34), new Person(28)};
The main issue now is that you have no way to refer to which person's age belongs to whom since you haven't attached a name field to any of these instances. You could do that easily:
public class Person {
private String name;
private String age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// getters for name and age
}
...then you can instantiate your Person with two values.
new Person("Bob", 23);
Now that we've addressed one concern (which was where to store the people in the first place), now we need a way to randomly access them.
For that, we can use Random#nextInt().
Random rand = new Random();
System.out.println("Person's age - " + people[rand.nextInt(people.length)]);
This will randomly pull a Person out of the array and print their age.
If you want to get a random person within the person class you could store a reference to each person created, and then select randomly from that list
public class Person {
// A List of every Person Created.
private static final List<Person> allPeople = new ArrayList<People>();
// A source of random numbers
private static final Random rand = new Random();
...
public Person(int age) {
...
// Every time we create a new Person, store a reference to that person.
addPerson(this);
}
// synchronized as ArrayLists are not thread safe.
private static synchronized addPerson(Person person) {
allPeople.add(person);
}
...
public static Person getRandomPerson() {
// Get a random number between zero and the size of the list.
int random = rand.nextInt(allPeople.size() -1);
return allPeople.get(random);
}
Now this code is not what I would do in a production environment but it the question sounds like an exercise. A better way would be to store the people created in a List in your Example class. But trying to answer the question as you asked it.

Why am I unable to add elements into a LinkedList in Java?

This is part of a lab I couldn't figure out... I can't figure out what is wrong in the addGrade method in the roster class, where I have to add a grade to a student, and if the student doesn't already exist, create a new student and then add the grade. Note that initially, this class didn't have the instance variable Student stu, which I added when trying to get things to work.
Student is provided with a constructor, student scores are saved in a linkedlist. I only put a part of the code here... it has methods to get student name, get score, add score, and get score average.
My code is no longer working after some edits... when it was partially working, it just overwrote the previous students with the latest one. Student a was added with grade 5, then student b with 7, then student a is added again with 10. This should have student a with 2 entries (5, 10) in the linkedlist. When I ran my code it only had student a with 10, but also didn't work completely.
public class Student {
private String name;
private List scores = new LinkedList<>();
public Student(String name)
{
this.name = name;
}
public void addGrade(int score)
{
scores.add(score);
}
public class Roster {
String name;
int score;
Student stu;
//Adds a grade to the end of a list of grades for the named student.
//Should work even if no student with this name has ever been seen before.
public void addGrade(String name, int score) {
Student temp = new Student(name);
stu.addGrade(score);
}
//Gets the specified grade from the named student's scores.
public int getGrade(String name, int index) {
int a = stu.getScore(index);
return a;
}
//Gets the average for the named student.
public double getAverage(String name) {
return stu.getAverage();
}
}
A roster is a list of students.
A student has a list of scores.
This is not all of the code you will need, just part of your Roster class and the addGrade() method there:
public class Roster {
List<Students> students = new LinkedList<Student>();
public void addGrade(String name, int score) {
// Student s = null;
// Search for existing student.
for (Student currentStu : students) {
if (currentStu.name.equals(name) {
s = currentStu;
}
}
if (s == null) {
//Student not in our roster. Add him.
s = new Student(name);
}
// Add the score to that student.
s.addGrade(score);
}
}
There's a lot of things going on with this code, but I'll give you some pointers on what could be going wrong. I strongly encourage you to reach out to your teacher, a tutor or a TA to close the loop on some things, but for the most part, here are some of the conceptual errors that I see.
A Student is the most basic piece of information. A Roster contains a collection of students. It is likely the case that you want a List<Student> in your Roster class. You don't require any other fields than that in Roster.
You should provide a way to add more scores to a specific student, but this particular aspect I'll leave for you to discuss with your teacher.
Your current linked list declaration of scores is untyped. This is frowned upon since you will generate unchecked warnings at compile time, and if you accidentally add a non-numeral into that linked list, you'll get an error at runtime when attempting to do math on it.
It's also interesting that you use a linked list instead of an array-backed list, since you intend to index into the list. For performance reasons, I would recommend using ArrayList.
With all of that said, you would want to type it as List<Integer> scores = new ArrayList<>().
You need a way to search for a student by name. This is a bit tricky since you're storing Student entries in there, but it can be done. I'll describe a very rudimentary approach to it, but I want you to take it from there:
Iterate over the collection of all students you have in your roster.
If a student's name matches the current instance, then you've found your student.
If there is no student contained in the list that has the name, then you have to return a number that represents that there is no student there. 0 would be bad for averages as you can have an average score of 0; perhaps -1 would work.
A Map<String,Student> is the key here:
public class Roster {
private final Map<String, Student> students = new HashMap<>();
//Adds a grade to the end of a list of grades for the named student.
//Should work even if no student with this name has ever been seen before.
public void addGrade(String name, int score) {
Student stu = students.get(name);
if (stu == null) {
stu = new Student(name);
students.put(name, stu);
}
stu.addGrade(score);
}
//Gets the specified grade from the named student's scores.
public int getGrade(String name, int index) {
Student student = students.get(name);
if (student == null) {
throw new IllegalStateException("Student not found: " + name);
}
return student.getScore(index);
}
//Gets the average for the named student.
public double getAverage(String name) {
Student student = students.get(name);
if (student == null) {
throw new IllegalStateException("Student not found: " + name);
}
return student.getAverage();
}
}

The object-oriented approach to a many-to-many relationship

I'm battling at the moment in trying to understand how to approach this issue in an object-oriented way.
With a many-to-many relationship such as Students-Subjects, where each student gets a mark for a certain subject, assuming the following:
I want to be able to display all the marks for a given student.
I want to display all the marks from different students for a given subject
I want to be able to change any student's mark for a given subject.
I have trouble with this last one, I can't seem to think of a way to relate the classes to each other so that the marks will remain congruent when changed...
Here's what I was thinking about doing in pseudocode. Pretend we have 3 students each involved in 3 subjects (9 marks total):
Make a class for Student (String name, int studNumber)
Make a class for Subject (String name, int subNumber)
Make a class for Result(int percentageScore String grade(calculated based on
percentageScore))
Then have an array of arrays of Result objects, eg. [1][2] in the array will
give the score for the student with studNumber 2 in the subject with subNumber 1.
I feel like this isn't object-oriented? There should be some kind of acknoledgement of the relationship within the class design for subject and students. If that is indeed right, could anyone point me in the right direction? How does one do this in an object-oriented way?
Thanks a lot.
Why go with such complex class structures. You can have a simple Student class.
class Student{
String stuName;
long rollNo;
public Student(String stuName, long rollNo){
this.stuName=stuName;
this.rollNo=rollNo;
}
.
.
.
}
And a Subject class. Each subject has certain students enrolled and the marks that each student has scored in that subject. Which can be represented as:-
class Subject{
String subName;
HashMap<Integer,Student> Result;
public Subject(String subName){
this.subName=subName;
Result=new HashMap<Integer,Student>();
}
//add methods to add students,modify marks, etc
public void addStudent(String name,long roll, int marks){
Result.put(marks,new Student(name,roll));
}
public int giveMarksForSubject(long roll){
//iterate Results , and check for student objects to match roll no. return key of matching student
.
.
.
}
.
.
}
For the part where you mentioned you want to change marks for students for certain subject. You can search the Subject object by String name in your Main method's class and then change Student's marks according to name/rollNo. You can provide methods in Subject class for implementing such functionality.
Both subjects and grades have a limited number of values, so I suggest using enums:
public class Example {
public static void main(String[] args) throws IOException {
Student s1 = new Student("John Doe");
s1.setGrade(Subject.MATHS, Grade.B);
s1.setGrade(Subject.PHYSICS, Grade.A);
s1.setGrade(Subject.ENGLISH, Grade.E);
Student s2 = new Student("Jane Smith");
s2.setGrade(Subject.MATHS, Grade.C);
s2.setGrade(Subject.PHYSICS, Grade.C);
s2.setGrade(Subject.ENGLISH, Grade.A);
// print students and their grades:
s1.printAllGrades();
s2.printAllGrades();
// print every subject and its grades:
for(Subject s : Subject.values()){
s.printAllGrades();
}
}
}
enum Subject{
MATHS, PHYSICS, ENGLISH;
private Map<Student, Grade> grades = new HashMap<Student, Grade>();
public void setGrade(Student student, Grade grade){
grades.put(student, grade);
}
public void printAllGrades(){
System.out.println(this);
for(Student s : grades.keySet()){
System.out.println(s.getName() + " : " + grades.get(s));
}
}
}
enum Grade{
A, B, C, D, E, F
}
class Student{
private String name;
private Map<Subject, Grade> grades = new HashMap<Subject, Grade>();
public Student(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setGrade(Subject subject, Grade grade){
grades.put(subject, grade);
subject.setGrade(this, grade);
}
public Grade getGrade(Subject subject){
return grades.get(subject);
}
public void printAllGrades(){
System.out.println("Grades of " + name + ":");
for(Subject s : grades.keySet()){
System.out.println(s + " : " + grades.get(s));
}
}
}
Using the enum type is suitable to list both subjects and grades. It guarantees that only suitable values can be passed as an argument and is easily extensible - you can add a method to an enum if you wish. A simple HashMap for every student is enough to hold the mappings between subjects and grades.
You may want to read more on enums in java.
I think the approach should be same like with database tables. You should implement some sort of a "joining class" between these 2. That class should be singleton and you should reference it in both, students and subjects. The class should have some sort of a list or a map, or something with that structure, which would contain properties: student, subject, mark. That way you could iterate through that collection by any of those properties, which should do what you need. This example How to make SQL many-to-many same-type relationship table is for databases, but I think it should give you some helpful insight.
This article makes a very compelling case for including the following structures in your design:
Student (incl an array of Result pointers)
Subject (incl an array of Result pointers)
Result (with all the attributes that belong to the relationship)
Though the original post was a long time ago, hope this helps someone else.

Connecting 3 different java files

First it starts off with the class College tester, it askes the user for a command.
The first command will be to add. what adding does is it askes the user to enter a name ( with at least 2 words by identifying a space) and an address.
Then it creates a student object with it. and I add the student object to a arraylist.
Issue #1: How would i add the collegetester input and create a student object out of it
Issue #2: after how would i add it to the array College
Collegetester (where i get user input)
import java.util.Scanner;
public class CollegeTester {
Scanner input = new Scanner(System. in );
private String command;
public String name;
public static void main(String[] args) {
CollegeTester collegeTester = new CollegeTester(); //creates object
collegeTester.getCommand(); //goes to command
}
//Ask user for a command
public void getCommand() {
System.out.println("Enter a command: ");
command = input.nextLine();
if (command.equals("add"))
addCommand(); //If command is add go to addcommand
}
//Add name and address to student object
public void addCommand() {
String name = "";
do {
System.out.println("Enter a Name: ");
name = input.nextLine();
} while (!(name.contains(Character.toString(' ')))); //To check if the name has at least 2 words
System.out.println("Enter an Address: ");
String address = input.nextLine();
Student student = new Student(name, address);
getCommand(); //repeat to see if user wishes to add another
}
}
Student object (The student object)
public Student(String name, String address) {
if (name == null)
name = "";
if (address == null)
address = "";
this.name = name;
this.address = address;
lastAssignedNumber++;
studentNum = lastAssignedNumber;
}
and the arraylist (In a different file)
import java.util.ArrayList;
public class College {
private ArrayList <College> entries = new ArrayList<College>();
}
Let's do a quick analysis of your code:
Your Student object looks like a constructor for the Student class object type which is most likely in this case an inner class of the CollegeTester class.
So here's the deal, your addCommand() already connects your CollegeTester class with your Student class, by executing this command after you provide the input for name and address it creates a new instance of the Student object.
This means that at this point, you need to add this newly created Student object to your College list.
However if you take a good look at your College list you will see that:
It's marked private (meaning it can be accessed only from within itself)
It is a list of College type objects (but you need a list of Student type objects)
So your options at this point are:
Make the list public
Create a public setter method that will do the adding
You will also need to change the list object type to Student if you wish to store Student objects in the list.
Also unless you want to have multiple colleges you might consider declaring your list as static otherwise you will have to declare an instance of it to add the Student objects to it.
Hopefully this is enough information to get you started in the right direction.
Also an advice is to not look at classes as actual objects, instead look at them as blueprints that can be used to construct those real objects.
The main method should call a constructor in the Object array class that create's the Object CollegeTester ct.
ct = new CollegeTester();
Then a method to insert new students, that calls the Student constructor in the student class to create a new student Object.
ct.insert("input1", "input2");
write an insert method that calls to create a new student and adds that object to College.
Constructor to add values to the new student object:
String studentName;
String studentAddress;
public Student(String name, String address) {
studentName = name;
studentAddress = address;
}
note: with ArrayList you could simply use the method add of ArrayList.

How does this code break the Law of Demeter?

The following code breaks the Law of Demeter:
public class Student extends Person {
private Grades grades;
public Student() {
}
/** Must never return null; throw an appropriately named exception, instead. */
private synchronized Grades getGrades() throws GradesException {
if( this.grades == null ) {
this.grades = createGrades();
}
return this.grades;
}
/** Create a new instance of grades for this student. */
protected Grades createGrades() throws GradesException {
// Reads the grades from the database, if needed.
//
return new Grades();
}
/** Answers if this student was graded by a teacher with the given name. */
public boolean isTeacher( int year, String name ) throws GradesException, TeacherException {
// The method only knows about Teacher instances.
//
return getTeacher( year ).nameEquals( name );
}
private Grades getGradesForYear( int year ) throws GradesException {
// The method only knows about Grades instances.
//
return getGrades().getForYear( year );
}
private Teacher getTeacher( int year ) throws GradesException, TeacherException {
// This method knows about Grades and Teacher instances. A mistake?
//
return getGradesForYear( year ).getTeacher();
}
}
public class Teacher extends Person {
public Teacher() {
}
/**
* This method will take into consideration first name,
* last name, middle initial, case sensitivity, and
* eventually it could answer true to wild cards and
* regular expressions.
*/
public boolean nameEquals( String name ) {
return getName().equalsIgnoreCase( name );
}
/** Never returns null. */
private synchronized String getName() {
if( this.name == null ) {
this.name == "";
}
return this.name;
}
}
Questions
How is the LoD broken?
Where is the code breaking the LoD?
How should the code be written to uphold the LoD?
I think that here are two problems:
Grades logic is too much mixed with Student. It should be done in Grades class
Teacher's logic is placed into Student.
Conclusion: Student knows too much about inner structure and logic of Teacher and Grades and that breaks LoD
Most problems such as this can be solved by revisiting your domain model.
It looks like the Student has way more responsibility than it should. It should have only one reason to change.
I would refactor this by adding a ReportCard object.
public class ReportCard
{
public Student Student...
public int Year...
public ReportCardItem[] ReportCardItems...
getGrades()...
createGrades()...
}
public class ReportCardItem
{
public Grade Grade...
public string Subject...
public Teacher Teacher...
}
Methods in class Student which break the Law of Demeter are
private Grades getGradesForYear( int year )
private Teacher getTeacher( int year )
because these expose domain objects Grades and Teacher to the application.
Assuming that you wish to continue to hide the Grades inside a Student and a Teacher inside Grades, one way to remedy this problem is to define proxy methods (also called delegate methods) in class Student that operate on the internal Grades and Teacher objects on behalf of the application, similar to method Student.isTeacher(int, String). This solution may lead to duplication of methods in Grades and Teacher in Student which is a disadvantage of a class design which respects the LofD.
A better solution would be to remove the Grades and Teacher from Student and put them all in another class, say Transcript:
class Transcript {
Student student;
Teacher teacher;
Grades grades;
Integer year;
}
Person.isTeacher "reaches through" according to the wikipedia article you mention.
I was surprised to find the list of grades a property of the student. Shouldn't that be something the school knows about and manages? I'd ask the school, which teacher graded a student in which year...
By having these two private functions breaks LoD.
private Grades getGradesForYear( int year )
private Teacher getTeacher( int year )
Students shouldn't need the logic to to perform such tasks.
The way I would redesign this is to separate data from logic. Student should purely be a data only. It should contain information about the student and student only. Therefore this does not include Grades as that concept requires others such as subject, and teacher.
The same goes for teacher. I would then create a place to store grade information and another place for subject information.
To perform similar tasks I would do this:
gradesDatabase.getGrade(subject, student);
subjectDatabase.getTeacher(subject, student);
Where subject is also a data only object.

Categories

Resources