[MAS] Mini projekt 1

Mój projekt pierwszy z baaardzo obszernym komentarzem.

Krótka instrukcja użycia:

mas_mp1

Wymagania co do projektu są tu:
http://www.users.pjwstk.edu.pl/~mtrzaska/Files/MAS/MAS-informacje-stacjonarne.pdf

Jak zrobić ‚ekstensje’ (są w projekcie, ale tu macie opis o co chodzi):

http://www.mtrzaska.com/plik/mas/mas-wyklad-nr-04

Projekt składa się z 4 plików:

Main.java

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.Random;


public class Main
{
	public static void main(String[] arg) throws Exception
	{
		// jesli wczesniej cos zapisalem [odpalam program po raz kolejny] to wczytaj to co bylo
		// DEMONSTRACJA DZIALANIA 'TRWALYCH EKSTENSJI' (trwale czyli nie znikaja po zamnieciu programu)
		if(new File("mojeTajneDane").isFile())
		{
			try
			{
				// plik jest w 'workspace'
				FileInputStream fileIn = new FileInputStream("mojeTajneDane");
				ObjectInputStream inStream = new ObjectInputStream(fileIn);
				// odczytuje wszystko z dysku do pliku 'mojeTajneDane' jedna linijka
				// 'magia javy' zrobi reszte
				ObjectPlus.odczytajEkstensje(inStream);
				inStream.close();
				fileIn.close();
			}
			catch(IOException i)
			{
				i.printStackTrace();
				return;
			}
			catch(ClassNotFoundException c)
			{
				System.out.println("Klasa nie znaleziona.");
				c.printStackTrace();
				return;
			}
		}
		
		
		
		// utworz nowego Grzegorza o wzroscie miedzy 165, a 195
		Random rand = new Random();
		Student studencik = new Student("Grzegorz" + rand.nextInt(100), 165 + (rand.nextInt(31)));
		// na date urodzenia ustawiamy czas jaki jest 'teraz'
		studencik.ustawDateUrodzenia(new Date());
		// wywoluje przeciazana metode z parametrem 'int'
		studencik.dodajOcene(2 + rand.nextInt(4));
		// wywoluje przeciazana metode z parametrem 'Double'
		studencik.dodajOcene(new Double(2 + ((double)rand.nextInt(7) / 2)));
		// pokaz wszystkich Grzegorzy jacy istnieja
		ObjectPlus.pokazEkstensje(Student.class);
		
		
		
		// przed zamknieciem programu zapisz wszystkie ekstensje do pliku
		// DEMONSTRACJA DZIALANIA 'TRWALYCH EKSTENSJI' (trwale czyli nie znikaja po zamnieciu programu)
		try
		{
			// plik jest w 'workspace'
			FileOutputStream fileOut = new FileOutputStream("mojeTajneDane");
			ObjectOutputStream outStream = new ObjectOutputStream(fileOut);
			// zapisuje wszystko na dysk do pliku 'mojeTajneDane' jedna linijka
			// 'magia javy' zrobi reszte
			ObjectPlus.zapiszEkstensje(outStream);
			outStream.close();
			fileOut.close();
		}
		catch(IOException i)
		{
			i.printStackTrace();
		}
		
	}
}

Osoba.java

import java.util.Date;


public abstract class Osoba extends ObjectPlus
{
	/*
	wymagane do serializacji, w przyszlosci jak cos zmienisz w tej klasie [dodasz/usuniesz atrybut]
	to zmienisz ta liczbe na inna, dzieki temu mechanizm 'serialize' nie pozwoli zaladowac 
	pliku z dysku z starymi danymi do programu ktory uzywa innej klasy
	(przeciez moze byc zupelnie inna klasa o nazwie Student i dane takie jak 'oceny' moga w niej
	nie wystepowac)
	 */
	private static final long serialVersionUID = 1L;
	/* PRZYPOMNIENIE: Co to jest 'protected'?
	 * To takie cos jak 'private' tylko, ze zmienna jest widziana w klasach 'dziedziczacych' (rozszerzajacych).
	 * Dzieki temu bedzie mozna wyswietlac np. 'imie' w studencie ktory dziedziczy po osobie.
	 */
	
	// atrybut prosty, wymagany, pojedynczy, obiektu
	protected String imie;
	/*
	 * WYMAGANY:
	 * zawsze kiedy go ustawiamy to sprawdzamy czy ktos nam nie przekazal 'null'
	 */
	
	// atrybut prosty, opcjonalny, pojedynczy, obiektu
	protected String nazwisko;
	/*
	 * OPCJONALNY:
	 * zawsze kiedy go wyswietlany to sprawdzany cyz ktos nam nie przekazal 'null'
	 * jesli 'null' to trzeba wyswietlic o tym informacje lub inaczej to 'obsluzyc',
	 * bo proba wyswietlenie 'null' skonczy sie bledem programu
	 */
	
	// atrybut zlozony, opcjonalny, pojedynczy, obiektu
	protected Date dataUrodzenia;
	/*
	 * ZLOZONY:
	 * to taki ktory zawiera w sobie wiecej niz jedna informacje
	 * ogolnie chodzi o odniesienie sie do innego obiektu (oprocz 'String')
	 * 
	 */
	// atrybut prosty, wymagany, pojedynczy, obiektu
	protected int wzrost = 0;
	/*
	 * jest tu tylko po to, zeby wyliczac srednia (i miec cos 'pochodnego')
	 */
	
	// atrybut prosty, pojedynczy, wyliczalny (pochodny), klasowy
	private static double sredniWzrost = 0;
	/*
	 * KLASOWY:
	 * dotyczy klasy, czyli wszystkich 'Osob', a nie jednej konkretnej
	 * technicznie: zmienna typu static
	 * 
	 * POCHODNY:
	 * mozna go wyliczyc, w tym wypadku wystarczylo by pobrac wszystkie Osoby z 'ekstensji' ObjectPlus
	 * pobrac ich wzrost i wyliczyc srednia, ale prosciej to po prostu pamietac
	 * i liczyc tylko raz przy dodawaniu osoby
	 */
	// atrybut prosty, pojedynczy, klasowy - potrzebny do liczenia sredniej
	private static int iloscOsob = 0;
	
	// konstruktor nie jest 'pusty', bo imieOsoby jest 'wymagane', wiec musi byc w kazdym konstruktorze
	Osoba(String imieOsoby, int wzrostOsoby)
	{
		/*
		 * BARDZO WAZNE: kazdy konstruktor klasy dziedziczacej po klasie ObjectPlus musi wywolywac 'super()'
		 * Co to jest 'super()'?
		 * Jest to cos co odpali konstruktor w klasie 'wyzej' / 'u rodzica' czyli w ObjectPlus.
		 * A co to robi?
		 * To zapewnia, ze kazdy utworzony obiekt jest na liscie 'ekstensji' w ObjectPlus.
		 * Konstruktor w ObjectPlus zajmuje sie dodawaniem/tworzeniem list ekstensji.
		 */
		super();

		
		
		// tutaj dbamy, aby wymagany atrybut byl ustawiony, a nie wskazywal na 'null'
		if(imieOsoby == null)
		{
			throw new NullPointerException("Osoba musi miec imie");
		}
		this.imie = imieOsoby;
		
		// 'int' nie moze byc 'nullem', wiec tego nie sprawdzamy
		// ale wymagamy podania tego w konstruktorze, wiec kazda osoba bedzie miala wpisany wzrost
		this.wzrost = wzrostOsoby;
		
		
		
		// to nam sie przyda do liczenia sredniej
		iloscOsob++;
		// a tu troche Drabikologi i mamy srednia
		sredniWzrost += (this.wzrost - sredniWzrost) / iloscOsob;
	}

	/*
	 * PRZESLANIANIE METOD:
	 * w nadklasie Osoba metoda ta zwraca tylko imie,
	 * a w Student imie z dodanym na poczatku tekstem 'Student'
	 * jaki tego sens? trudno mi wymyslic jakas sensowna przeslaniana funkcje
	 * 
	 * technicznie: w klasie dziedziczacej jest metoda o takiej samej nazwie, takich samych argumentach,
	 * ale zwraca cos innego
	 */
	public String getImie()
	{
		return this.imie;
	}
	
	/*
	 * METODA KLASOWA:
	 * metoda operujaca na zmiennych klasowych, a nie obiekcie
	 * technicznie: typu static
	 */
	public static double getSredniWzrost()
	{
		return sredniWzrost;
	}

	/*
	 * demonstracja roznicy w poslugiwaniu sie atrybutami 'wymaganymi' i 'opcjonalnymi'
	 */
	public void ustawImie(String imie)
	{
		if(imie == null) // uwaga wymagany!
		{
			throw new NullPointerException("Osoba musi miec imie");
		}
		this.imie = imie;
	}
	
	public void ustawNazwisko(String nazwisko)
	{
		this.nazwisko = nazwisko;
	}
	
	public void ustawDateUrodzenia(Date dataUrodzenia)
	{
		this.dataUrodzenia = dataUrodzenia;
	}
	
	public String toString()
	{
		String ret = new String();
		ret += "Imie: " + getImie();
		ret += "\nNazwisko: ";
		if(nazwisko != null) // uwaga opcjonalny!
			ret += nazwisko;
		else
			ret += "bez nazwiska";

		ret += "\nWzrost: " + wzrost;

		ret += "\nData urodzenia: ";
		if(dataUrodzenia != null) // uwaga opcjonalny!
			ret += dataUrodzenia;
		else
			ret += "nie znana";
		return ret;
	}
}

Student.java

import java.util.Vector;


public class Student extends Osoba
{
	/*
	wymagane do serializacji, w przyszlosci jak cos zmienisz w tej klasie [dodasz/usuniesz atrybut]
	to zmienisz ta liczbe na inna, dzieki temu mechanizm 'serialize' nie pozwoli zaladowac 
	pliku z dysku z starymi danymi do programu ktory uzywa innej klasy
	(przeciez moze byc zupelnie inna klasa o nazwie Student i dane takie jak 'oceny' moga w niej
	nie wystepowac)
	 */
	private static final long serialVersionUID = 1L;
	
	// atrybut prosty, powtarzalny, obiektu
	private Vector<Double> oceny = new Vector<Double>();
	/*
	 * POWTARZALNY:
	 * obiekt zawiera 0 lub wiecej takich 'elementow'
	 * technicznie: lista/tablica
	 */
	
	Student(String imieOsoby, int wzrostOsoby)
	{
		/*
		 * 'super', bo dziedziczy po klasie ktora dziedziczy po ObjectPlus
		 * z parametrem 'imieOsoby' i 'wzrostOsoby', bo takie atrybuty sa wymagana w Osoba
		 */
		super(imieOsoby, wzrostOsoby);
	}
	
	/*
	 * PRZESLANIANIE METOD:
	 * w nadklasie Osoba metoda ta zwraca tylko imie,
	 * a w Student imie z dodanym na poczatku tekstem 'Student'
	 * jaki tego sens? trudno mi wymyslic jakas sensowna przeslaniana funkcje
	 * 
	 * technicznie: w klasie dziedziczacej jest metoda o takiej samej nazwie, takich samych argumentach,
	 * ale zwraca cos innego
	 */
	public String getImie()
	{
		return "Student " + this.imie;
	}
	
	/*
	 * PRZECIAZANIE METOD:
	 * metody maja taka sama nazwe, ale przyjmuja rozne parametry
	 * w tym przypadku jedna 'Double', a druga 'int'
	 */
	public void dodajOcene(Double ocena) throws Exception
	{
		if(ocena == null || ocena < 2 || ocena > 5)
		{
			throw new Exception("Ocena musi byc pomiedzy 2, a 5");
		}
		oceny.add(ocena);
	}
	public void dodajOcene(int ocena) throws Exception
	{
		if(ocena < 2 || ocena > 5)
		{
			throw new Exception("Ocena musi byc pomiedzy 2, a 5");
		}
		oceny.add(new Double(ocena));
	}
	
	public String toString()
	{
		// na poczatek opisu studenta wklejamy opis z Osoby (imie, nazwisko..) wywolujac metode z klasy 
		// z ktorej dziedziczymy, robimy to uzywajac 'super'
		String ret = super.toString();
		ret += "\nOceny: ";
		if(oceny.size() > 0)
		{
			for(Double ocena : oceny)
			{
				ret += ocena + ",";
			}
		}
		else
		{
			ret += "brak ocen";
		}
		return ret;
	}
}

ObjectPlus.java

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Vector;

/* 
 * To jest klasa z wykladu nr 4 z drobnymi zmianami, zeby eclipse nie wywalal 'ostrzezen'
 * --------------------
 * klasa zrobina przez wykladowce!, wiec raczej nie powinien sie czepiac
 * ale trzeba rozumiec KAZDY szczegol jej dzialania!
 * */
public class ObjectPlus implements Serializable
{
	/*
	wymagane do serializacji, w przyszlosci jak cos zmienisz w tej klasie [dodasz/usuniesz atrybut]
	to zmienisz ta liczbe na inna, dzieki temu mechanizm 'serialize' nie pozwoli zaladowac 
	pliku z dysku z starymi danymi do programu ktory uzywa innej klasy
	(przeciez moze byc zupelnie inna klasa o nazwie Student i dane takie jak 'oceny' moga w niej
	nie wystepowac)
	 */
	private static final long serialVersionUID = 1L;
	/*
	(w tym tlumaczeniu bede uzywal slowa 'tablica' zamiast 'lista', roznica? lista [z ktorej korzystamy pod nazwa Hashtable!]
	sama 'rosnie' jak do niej dodajemy obiekty, a tablice maja staly rozmiar np. 'new Integer[5]' - 5 elementow)
	
	Tlumaczenie typow:
	1. Class<? extends ObjectPlus> - obiekt typu Class pobrany z obiektu ktory rozszerza ObjectPlus, czyli np. Osoba lub Student,
	String czy Integer nie rozszerzaja ObjectPlus, wiec nie zostana zaakceptowane
	2. Vector<ObjectPlus> - lista/kontener obiektow typu ObjectPlus, do takiej listy mozna wsadzic dowolny obiekt
	ktory rozszerza ta klase, czyli np. Student czy Osoba
	
	Opis:
	To jest 'tablica tablic', a dokladniej tablica ktora jako klucz nie ma liczby, a.. KLASE (Class)!
	Class to o dziwo w Javie klasa ktora przechowuje informacje o klasie, mozna pobrac obiekt typu Class z kazdego obiektu
	w Javie uzywajac:
	Class klasaObiektu = mojObiekt.getClass();
	No wiec mamy juz tablice ktora uzywa jako klucza Class, a jakie dane przechowuje? Przechowuje Vector<ObjectPlus>,
	czyli tablice obiektow typu ObjectPlus:
	1. Osoba rozszerza ObjectPlus [extends], wiec tez moga ja wrzucic do tej tablicy
	2. Student rozszerza Osoba ktora rozszerza ObjectPlus, wiec tez moge ja tam wrzucic
	
	A po co mi wogole tablica tablic zamiast zwyklej tablicy?
	Bo ObjectPlus w projekcie docelowym moze byc rozszerzany przez wiele obiektow roznych
	klas jak np. 'Wplata', 'Ocena', 'Przedmiot' itd., a nie chce mi sie pisac nowego ObjectPlus dla kazdej z nich
	Nie chce tez przechowywac wszystkich obiektow w jednej tablicy [typu Object, wtedy moge tam wrzucic wszystko],
	bo wtedy 'pobranie wszystkich 'Student' wymagalo by przegladania wszystkich elementow tablicy i
	sprawdzania ktorzy to Studenci.
	 */
	private static Hashtable<Class<? extends ObjectPlus>, Vector<ObjectPlus>> ekstensje = new Hashtable<Class<? extends ObjectPlus>, Vector<ObjectPlus>>();

	/**
	 * Konstruktor.
	 */
	public ObjectPlus()
	{
		Vector<ObjectPlus> ekstensja = null;
		Class<? extends ObjectPlus> klasa = this.getClass();

		if(ekstensje.containsKey(klasa))
		{
			// Ekstensja tej klasy istnieje w kolekcji ekstensji
			// jest juz jakis np. Student, wiec jest i lista studentow, nie chce tworzyc nowej listy,
			// tylko dodac nowego studenta do listy juz istniejacej
			ekstensja = ekstensje.get(klasa);
		}
		else
		{
			// Ekstensji tej klasy jeszcze nie ma -> dodaj ją
			// to wystepuje kiedy pierwszy raz dodajemy obiekt danego typu (np. Student) do ekstensji
			ekstensja = new Vector<ObjectPlus>();
			ekstensje.put(klasa, ekstensja);
		}
		// a tu wreszcie dodaje studenta do listy nowej LUB istniejacej
		ekstensja.add(this);
	}
	
	/**
	 * Zapisuje wszystkie ekstensje do podanego strumienia (wersja z serializacja).
	 * Metoda klasowa.
	 * @throws IOException 
	 */
	public static void zapiszEkstensje(ObjectOutputStream stream) throws IOException
	{
		/*
		'magia javy' zamienia obiekt (Hashmape) zawierajacy inne obiekty w tekst,
		wiec jak zawiera liste Studentow to Ci studenci maja np. atrybut 'Date dataUrodzenia' i to tez sie zapisze
		(Date jest 'serializable' jak wiekszosc klas uzywanych na codzien w javie)
		(wszystkie obiekty jakie zawiera ekstensja musza rozszerzac interfejs 'Serializable')
		java znajdzie wszystkie powiazane obiekty z tymi obiektami ktore mamy w hashmap 'ekstensje' i 
		wszystkie powiazane z ich powiazaniami itd. i wszystko zapisze jako tekst!
		a potem jeszcze moze z tego tekstu odtworzyc ten obiekt [i obiekty powiazane], magia!
		
		UWAGA: to zapisze tylko zmienne 'obiektu', zmienne 'klasowe' (te z 'static') nie zostana zapisane i
		to jeden z problemow z takim prostym zapisem, ale mozna samemu dopisac zapis 'static'
		na szczescie to nie jest wymagane w MP_1, a jedynie w projekcie semestralnym
		*/
		stream.writeObject(ekstensje);
	}

	/**
	 * Odczytuje wszystkie ekstensje z podanego strumienia (wersja z serializacja).
	 * Metoda klasowa.
	 * @throws IOException 
	 * @throws ClassNotFoundException 
	 */
	@SuppressWarnings("unchecked")
	public static void odczytajEkstensje(ObjectInputStream stream) throws IOException, ClassNotFoundException
	{
		/*
		tutaj 'magia javy' odtwarza dane zapisane jako tekst do pliku (zapisane w funkcji wyzej)
		 */
		ekstensje = (Hashtable<Class<? extends ObjectPlus>, Vector<ObjectPlus>>) stream.readObject();
	}	
	
	/**
	 * Wyswietla ekstensje.
	 * Metoda klasowa.
	 * @throws Exception 
	 */
	public static void pokazEkstensje(Class<? extends ObjectPlus> klasa) throws Exception
	{
		/*
		wystarczy gdzies w programie wywolac ta funkcje tak:
		ObjectPlus.pokazEkstensje(Student.class);
		aby wyswietlic wszystkie obiekty typu 'Student' istniejace w programie
		 */
		Vector<ObjectPlus> ekstensja = null;
		
		if(ekstensje.containsKey(klasa))
		{
			// Ekstensja tej klasy istnieje w kolekcji ekstensji
			ekstensja = ekstensje.get(klasa);
		}
		else
		{
			throw new Exception("Unknown class " + klasa);
		}
		
		System.out.println("Ekstensja klasy: " + klasa.getSimpleName());

		for(Object obiekt : ekstensja)
		{
			System.out.println(obiekt + "\n");
		}
	}
	
}

Mam nadzieję, że będziecie przestrzegać instrukcji.

Wszystko w .zip:

MAS_MP1_Jerzego

Ten wpis został opublikowany w kategorii PJWSTK – MAS i oznaczony tagami , , . Dodaj zakładkę do bezpośredniego odnośnika.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *