Gå til innhold

Hjelp til hvordan å lage et godt klassehiraki


Anbefalte innlegg

Hei, står litt fast på hvordan jeg skal lage et godt klassehiraki med lignende beskrivelser/funksjoner.

 

Systemet skal ta hånd om personer.

Noen er ansatt, noen er studenter, noen er begge og andre er ingen.

 

Slik jeg har tenkt hittil, så har vi en superklasse Person, med to subklasser Ansatt og Student. Om du verken er ansatt eller student, er du en person.

 

Men det jeg sliter med er hvis du er begge, og dermed trenger variabler og metoder fra klasser(her Ansatt og Student).

Kan alltids lage en 3. klasse, AnsattOgStudent og extende Person, men da blir det mye duplikat av kode + jeg synes det blir helt feil.

 

Tenkte litt på å ha noe i et interface, men variabler ol. blir automatisk public, static, final så vidt jeg husker, så da funker ikke det.

Føler også det blir litt feil.

 

Det jeg helst vil ha, er en klasse, AnsattOgStudent, som arver fra både Ansatt og Student.

Har da enkelt tilgang til alt som trengs, blir ikke my duplikat av kode, og kan enkelt sjekke hvem som er hvem, men usikker på hvordan det skal lages/gjøres.

 

Noen som er noen forslag til en god måte å få til dette på?

Endret av Fred7555
Lenke til kommentar
Videoannonse
Annonse

Noe av det viktigste du må lære deg i objektorientert programmering er: arv er roten til alt ondt! Arv er et kraftig verktøy, men er noe som bør brukes sparsomt. Egentlig burde det være forbudt å bruke arv før du har hatt minst 5 års jobb erfaring innen objektorientert programmering, siden man kan gjøre kode uleselig og lite gjenbrukbar hvis man ikke vet hva man gjør.

 

I eksempelet ditt ovenfor trenger man ikke arv. Egentlig ville det ha vært straffbart med 10 piskeslag hvis du hadde brukt arv slik du har foreslått. Hvorfor? Hva skjer hvis en student blir en ansatt og student? Da må du lage et nytt objekt av en annen klasse og fjerne det gamle objektet. Forferdelig lite dynamisk.

 

Person skal ikke arves, punktum. Derimot kan du bruke referenser, interfaces og delegates. Det kommer an på hvordan du vil organisere dette. Personen kan ha egenskaper som "Studiegang" og "Jobb". Hvis en person har en aktiv studiegang, er han en student. Ikke klassen Student, men metoden "erStudent()". Samme for ansatt og "Jobb". Slik kan du legge til eller fjerne jobb og studiegang, og dermed dynamisk endre egenskapen til Person. Kanskje har en person flere studieganger og/eller flere jobber? Studentassistent i Matte og studentassistent i programmering, og studerer elektro og fysikk? Da kan man bruke lister av studieganger og jobber.

Hvis du har separate lister av studenter og ansatte, kan du istedet la objektet Ansatt referere Person, og Student referere Person. Slik kan en Ansatt og en Student være den samme personen.

Lenke til kommentar

Hadde tenkt noe lignende i starten, men i oppgaven er det noen krav slik at det dessverre ikke er mulig, som jeg glemte å nevne.

 

Det kreves at det er subklasser (og eventuelt interfaces) i hirakiet.

Og en ansatt skal ha en liste over sine studenter (ansatt som lærer/underviser/professor), og det er bare ansatt som skal ha en slik liste, ingen andre.

Om jeg da bare har alt i en klasse, med metoder og variabler som bestemmer tilstanden, så vil fremdeles ansatt ha en slik liste(kan "gjemme" den, men den vil fremdeles være der).

Endret av Fred7555
Lenke til kommentar

Kankje du kan gjøre sånn?

 

http://dystopiancode.blogspot.no/2012/01/simulating-multiple-inheritance-in-java.html

 

Og der hvor det blir mye kodeduplisering tar du i bruk delegater, sånn som Patton foreslo. (http://en.wikipedia.org/wiki/Delegation_pattern)

 

Arv er som Patton skriver litt "oppskrytt", hvis du kun bruker det som eneste mekanisme for å strukturere koden vil du ende opp med uoversiktelige arvehierarkier (særlig om du hadde hatt multippel arv tilgjengelig) og kode som er hardt bundet sammen, og ikke modulær.

 

Interfaces inneholder ikke implementasjonskode og dermed er ulempene med hierarki dem imellom mindre. Hvis du i tillegg benytter delegater for å unngå duplisert kode, så tvinges du nærmest til å kode mer modulært enn du ellers ville gjort, du slipper å ende opp med store monolittiske klasser som burde vært refaktorert ut i mindre moduler, rett og slett fordi språket "tvinger" deg til å gjøre det riktitg i utgangspunktet.

Lenke til kommentar

Etter litt tenking, tror jeg at jeg har kommet fram til en brukbar løsning.

Men en ting jeg lurer på.

 

Jeg velger å kun ha én klasse, Person, istedenfor å ha en superklasse og subklasser.

Person implements IAnsatt, IStudent, slik at den har metodene som trengs.

Når jeg lager objektet, så lager jeg enten av type IAnsatt, IStudent eller Person.

På den måten har den bare adgang til de metodene som er ment for typen.

 

Men siden det er mulig å være verken ansatt eller student, så må jeg ha en mulighet for at objektet ikke skal ha adgang til noen av metodene.

Hittil har jeg laget et tomt interface, implementert det i Person, og bruker det om personen er verken student eller ansatt.

Men usikker på om det er den beste måten?

 

Tenkte også litt på å ha diverse boolean og metoder for å bestemme typen(ansatt, student, begge eller ingen), men da må jeg sjekke i hver metode hvilken rolle objektet har + at det har tilgang til metodene.

I mitt hode virker metoden med interface bedre.

 

Virker metoden min hittil (med ineterface) som en god/brukbar metode?

Og hvis ja, er metoden med et tomt interface den beste måten å lage objetet uten noen roller, men samtidig av klassen Person?

 

Takk for hjelpen hittil :)

Endret av Fred7555
Lenke til kommentar

først; dette vil jo i følge deg selv ikke kvalifisere som besvarelse på oppgaven (ingen subklasser). men jeg syns du bør argumentere både overfor deg selv og læreren for det motsatte. metoden med tomt IPerson-interface syns jeg høres helt utmerket ut :)

 

i enkelte tilfeller hvor det er ønskelig med litt varierende egenskaper, men blir overkill med klassehierarkier og/eller interfacer kan du bruke enums. Enums kan ikke inngå i arvehierarkier, men enums kan ha metoder, også abstrakte, og disse kan overrides på ulik måte i de ulike verdiene

 

enum Mood {
  HAPPY {
     @Override public String getFace() { return ":-)"; }
  },
  SAD {
     @Override public String getFace() { return ":-("; }
  };
  public abstract String getFace();
}

Lenke til kommentar

Har nå møtt to problemer som gjør at løsningen med forskjellige interfaces ikke funker.

 

- Jeg må kunne sjekke hvilken rolle en person har, f.eks. om personen er ansatt eller student.

 

Prøvde først med instance of, men den ser kun ut til å sjekke om interfaces er implementert i klassen.

Alle interfacesene er jo implementert, bare objektet/pekeren av av typen interfaces, så metoden returnerer true uansett hvilket av interfacene jeg tester.

 

- Alle objektene skal kunne legges inn i en beholder, f.eks. HashMap eller en List.

Om jeg f.eks. bruker klassen Person som parameter, så vil den kun akseptere objekter av Person eller dens subklasser, og ikke objekter av typen interface som Person implementerer.

Om de skal inn i listen, så må de kastes til Person, noe som gjør fjerner spesialiseringen/interface-pekeren, som jeg må ha.

 

Finnes det noen gode løsninger på problemene ovenfor, slik at jeg kan fortsette metoden med interface?

 

Eller må jeg bruke super- og subklasser igjen?

- Hvis ja, hva er en god måte å løse det på, mens det samtidig overholder kravene?

 

Sammendrag av kravene:

 

 

Programmet skal holde rede over personer. Person kan være ansatte, studenter, begge deler, eller ingen av delene.

Ansatte skal ha en liste av alle sine studenter, ha en metoder for å legge til og fjerne fra denne listen, og ha en metode for å undervise.

Studenter skal ha en metode for å undervise, men om de blir tatt for det (i en sjekk i slutten av programmet), så skal noe skje.

 

Slik jeg tenker, så må jeg kunne skille hvilken rolle de har, slik de bare kan ha tilgang til det de skal ha.

Må også kunne sjekke hvilken rolle de har.

De må også kunne legges inn i en generisk liste.

 

 

 

Takk for hjelpen :)

Lenke til kommentar

Forslag ... Litt uheldig med alle disse ifStudent og ifAnsatt-kallene, noen forslag til å eliminere bruken av disse?

 

package javaapplication2;
import java.util.ArrayList;
import java.util.List;
class PersonManager {
List<Person> persons;
// ....
}
public class Person implements IAnsatt, IStudent, IPerson, IAnsattAndStudent {
private String personnummer;
private final IAnsatt ansattDelegate;
private final IStudent studentDelegate;
private Person() {
	this.ansattDelegate = null;
	this.studentDelegate = null;
}
private Person(IAnsatt ansattDelegate) {
	this.ansattDelegate = ansattDelegate;
	this.studentDelegate = null;
}
private Person(IStudent studentDelegate) {
	this.ansattDelegate = null;
	this.studentDelegate = studentDelegate;
}
private Person(IAnsatt ansattDelegate, IStudent studentDelegate) {
	this.ansattDelegate = ansattDelegate;
	this.studentDelegate = studentDelegate;
}
public static IStudent createStudent() {
	return new Person(new Student());
}
public static IAnsatt createAnsett() {
	return new Person(new Ansatt());
}
public static IAnsattAndStudent createAnsattAndStudent() {
	return new Person(new Ansatt(), new Student());
}
public static IPerson createPerson() {
	return new Person();
}
@Override
public boolean isAnsatt() {
	return ansattDelegate != null;
}
@Override
public boolean isStudent() {
	return studentDelegate != null;
}
@Override
public String getPersonnummer() {
	return personnummer;
}
@Override
public void setPersonnummer(String personnummer) {
	this.personnummer = personnummer;
}
@Override
public int getSalary() {
	if (isAnsatt()) {
		return ansattDelegate.getSalary();
	} else {
		throw new RuntimeException("Person is not Ansatt");
	}
}
@Override
public void setSalary(int salary) {
	if (isAnsatt()) {
		ansattDelegate.setSalary(salary);
	} else {
		throw new RuntimeException("Person is not Ansatt");
	}
}
@Override
public int getPoints() {
	if (isStudent()) {
		return studentDelegate.getPoints();
	} else {
		throw new RuntimeException("Person is not Student");
	}
}
@Override
public void setPoints(int points) {
	if (isStudent()) {
		studentDelegate.setPoints(points);
	} else {
		throw new RuntimeException("Person is not Student");
	}
}
@Override
public List<IStudent> getStudents() {
	if (isAnsatt()) {
		return ansattDelegate.getStudents();
	} else {
		throw new RuntimeException("Person is not Ansatt");
	}
}
@Override
public void doTeach(String pensum) {
	if (isAnsatt()) {
		ansattDelegate.doTeach(pensum);
	} else {
		throw new RuntimeException("Person is not Ansatt");
	}
}
@Override
public void doStudy(String pensum) {
	if (isStudent()) {
		studentDelegate.doStudy(pensum);
	} else {
		throw new RuntimeException("Person is not Student");
	}
}
public static class Ansatt implements IAnsatt {
	private int salary;
	private List<IStudent> students = new ArrayList<>();
	@Override
	public int getSalary() {
		return this.salary;
	}
	@Override
	public void setSalary(int salary) {
		this.salary = salary;
	}
	@Override
	public void doTeach(String pensum) {
		for (IStudent s : students) {
			s.doStudy(pensum);
		}
	}
	@Override
	public List<IStudent> getStudents() {
		return students;
	}
}
public static class Student implements IStudent {
	private int points;
	private List<String> memory = new ArrayList<>();
	@Override
	public int getPoints() {
		return this.points;
	}
	@Override
	public void setPoints(int points) {
		this.points = points;
	}
	public void doTeach(String pensum) {
		throw new RuntimeException("Student caught teaching");
	}
	@Override
	public void doStudy(String pensum) {
		memory.add(pensum);
	}
}
}
interface IPerson {
String getPersonnummer();
void setPersonnummer(String personnummer);
boolean isAnsatt();
boolean isStudent();
}
interface IAnsatt {
int getSalary();
void setSalary(int salary);
List<IStudent> getStudents();
void doTeach(String pensum);
}
interface IStudent {
int getPoints();
void setPoints(int points);
void doStudy(String pensum);
}
interface IAnsattAndStudent extends IStudent, IAnsatt {
}

Endret av quantum
Lenke til kommentar

Jeg velger å kun ha én klasse, Person, istedenfor å ha en superklasse og subklasser.

Person implements IAnsatt, IStudent, slik at den har metodene som trengs.

Når jeg lager objektet, så lager jeg enten av type IAnsatt, IStudent eller Person.

På den måten har den bare adgang til de metodene som er ment for typen.

 

Her har du nok missforstått. Når du sier new Person() så får du et person-objekt, uansett om du refererer til det som Person, IStudent eller IAnsatt. Alle metodene "er der", du styrer kun hvilke metoder du velger å bruke der og da... Dette er ingen god løsning.

Lenke til kommentar

En mulig løsning (pseudokode):

 

class Person {
 List<IRole> roles;
}
interface IRole {}
class Student : IRole {}
class Employee : IRole {}
// og hvis du vil kan du eventuelt lage en klasse som kombinerer
// student og employee - hvis den kombinasjonen skal ha noen
// egne egenskaper som ikke hører hjemme i noen av dem hver
// for seg
class EmployeeAndStuden : IRole {
 private Student student;
 private Employee employee;
 // eksponer det som er aktuelt i public methods..
}

 

Den siste biten der er noe du antagelig vis ikke ønsker... :)

 

Men hvordan dette bør struktureres krever mer informasjon enn det du har fått. En objektstruktur skal ikke beskrive den virkelige verden, men være strukturert for best å ivareta oppførselen du trenger i programmet ditt. Oppførselen er ikke beskrevet.

Endret av torbjørn marø
Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
×
×
  • Opprett ny...