Diskuze: Pomoc pri OOP
V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.
Asi tě zklamu, ale nikde jsem si nevšiml, že bys používal OOP(zapouzdření,dědičnost a polymorfismus). Používání objektů(instance) není OOP(orientované objektové programování).
Ernest Klonkay:29.2.2016 14:18
Ako by to teda po spravnosti malo vyzerat s pouzitim OOP? Ako som pisal som zaciatocnik a snazim sa pouzivat "OOP"
OOP (3 pilíře OOP):
1 - zapouzdření ( použití modifikátoru přístupu v třídách např.
public,protected,private, + getter/setter)
2 - dědičnost ( použití dědičnosti - tvz. extends )
3 - polymorfismus ( použití polymorfismu přes dědičnost nebo rozhraní
)
Projdi si tu tutoriály, nejsou špatné ( http://www.itnetwork.cz/…olymorfismus) a ( http://www.itnetwork.cz/…tery-settery ) a pak teprve přemýšlej jak využít OOP. Existují úlohy ve, kterých se všechny pilíře OOP prostě nevyužijí.
- class Kocka - zde máš spoustu setterů/getterů ale jsou k ničemu neboť k proměnným v třídě je umožněn přístup.
private double objem, povrch, a, b, c;
private boolean maObjem = false, maPovrch = false, maA = false;
... toto je správně využití 1. pilíře tvz. zapouzdření
- Protože v příkladu máš jenom objekt Kocka pak prakticky nemůžeš využít dědičnost.
Kdybys měl třídu class Zvire a pak class Kocka extends Zvire pak toto je
příklad dědičnosti
... toto je správně využití 2. pilíře tvz. dědičnosti
- Polymofismus ve svém příkladu nemůžeš využít k tomu je potřeba vice typu např. Kocka,Pes, Krava buď děděných od Zvire nebo obsahující nějaké rozhraní např. interface Vzorce.
Atrament:1.3.2016 10:49
Koukám, že kolegu výše taky zmátla ta 'Kocka' - taky jsem hezkých pár chvil přemýšlel, co tam dělá Kočka (slovensky Mačka) mezi těmi kvádry a tělesy
Objektově orientovaný přístup je především postaven na tom, že modeluješ logiku aplikace tak, aby její součásti (třídy, moduly, balíčky,...) co nejvíce odpovídaly modelu realného světa. S tím souvisí i vztahy mezi těmito součástmi - taky by měli co nejvíce odpovídat těm v reálném světě. Pokud máš třídu Kocka co implementuje nějaký interface Vzorce, tak na první pohled vidíš že to spolu moc nesouvisí, copak Kocka obsahuje nějaké Vzorce?
Ve tvém případě modeluješ Tělesa u nichž tě zajímají Obsah a Povrch. Každé Těleso má nějaké rozměry a u každého se dá spočítat jeho Obsah a Povrch, existují ale různé druhy Teles s různými rozměry a různými metodamy jak vypočítat jejich Obsah a Povrch.
Pro vyjádření této situace se obvykle používá dědičnost. Máš třídu Teleso a pak další třídy jako Kocka, Valec, Kvadr atd, které od té třídy Teleso dědí nejaké základní společné rysy. V Tělese budeš mít abstraktní metody pro výpočty, které potom konkrétně implementuješ v těch potomcích. Smysl je v tom, že pak budeš moct udělat něco takového:
Teleso kocka = new Kocka(10); //tvorime kocku o velikosti strany 10
Teleso kvadr = new Kvadr(10,20,30) // tvorime kvadr o velikostech stran 10, 20, 30
System.out.println("Kocka ma obsah: " + kocka.getObsah());
System.out.println("Kvadr ma obsah: " + kvadr.getObsah());
Všimni si, že v obou výpisech jsem použil stejnou metodu getObsah() a vůbec mě nezajímalo jestli jde o kocku nebo kvádr.
mayo505:1.3.2016 11:17
Ako už písali vyššie, to rozhranie moc nedáva zmysel, je príliš veľké. Porušuje to interface segregation principle, ktoré hovorí, že nemáš mať veľké rozhrania, lebo ťa to núti implementovať aj veci ktoré nepotrebuješ (napríklad setB, setC) ... tým pádom to poruší aj liskov substitution principle, ktorý hovorí síce hovorí o tom, že tam kde sa očakáva nadtrieda môže byť použitá podtrieda, ale platí to aj pre interfaci. Ak niekde očakávaš interface Vzorce a budeš využívať všetky jeho možnosti (teda setA, setB, setC s rôznymí číslami) poruší ti to logiku tej kocky. Kvôli tomuto by som bol veľmi opatrný aj s dedičnosťou.
Ďalej funkcie ako setObjem a setPovrch ti porušujú zapúzdrenie a neviem si predstaviť čo to spraví u kvádru (vznikne nekonzistentnosť pretože, dĺžky strán nebudeš vedieť a aj tak môžeš použiť tú funkciu).
Funkcie vypocitajObjem a vypocitajPovrch sú zbytočné, stačí ti getObjem a getPovrch a tam urobiť výpočet
Funkcia info, nemá čo v triede Kocka robiť
Funkcia volbaVypoctu je tiež veľmi divná.
Čiže ako by sa to dalo spraviť? V rozhraní nechať len funkcie getObjem() a getPovrch(). Objekt Kocka by mal fungovať ako skutočná kocka, čiže napr. bez funkcií setB() a bez funkcií ktoré s kockou nemajú nič spoločné napr. info. Navyše kocku by som vytváral už asi parametrom v konštruktore (bez setA), JavaBeany sú trochu kontroverzné, pretože máš nekonzistentný stav (síce len pár riadkov, ale máš kocku bez strany)
Bol som tyzden mimo, tak skusil som to vylepsit alebo resp. prerobit podla vasich rad. Dufam, ze tentokrat uz to je lepsie, bol by som rad, ak by mi to niekto zhodnotil.
package vypoctytelies;
import java.util.Scanner;
public class VypoctyTelies {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
double povrch, objem;
double a, b, c;
int menu, menukocka, menukvader;
System.out.println("VÝPOČTY TELIES");
System.out.println("Zvolte teleso:\n1 <- Kocka\n2 <- Kváder");
menu = Integer.parseInt(in.nextLine());
if(menu >= 1 && menu <= 2) {
if(menu == 1) {
System.out.println("Vybrali ste si kocku");
System.out.println("Zvolte výpočet:\n1 <- Poznám stranu a\n2 <- Poznám objem\n3 <- Poznám povrch");
menukocka = Integer.parseInt(in.nextLine());
if(menukocka >= 1 && menukocka <= 3) {
if(menukocka == 1) {
System.out.println("Zadajte stranu a");
try {
a = Double.parseDouble(in.nextLine());
Tvary3D kocka = new Kocka(a);
System.out.println("Strana a je: " + kocka.getStranuA());
System.out.println("Uhlopriečka je: " + kocka.getUhlopriecka());
System.out.println("Povrch je: " + kocka.getPovrch());
System.out.println("Objem je: " + kocka.getObjem());
}
catch(NumberFormatException ex) {
System.out.println("Nezadali ste číslo!");
System.out.println(ex.getMessage());
}
}
else if(menukocka == 2) {
System.out.println("Zadajte objem");
try {
objem = Double.parseDouble(in.nextLine());
Tvary3D kocka = new Kocka();
kocka.setObjem(objem);
System.out.println("Strana a je: " + kocka.getStranuA());
System.out.println("Uhlopriečka je: " + kocka.getUhlopriecka());
System.out.println("Povrch je: " + kocka.getPovrch());
System.out.println("Objem je: " + kocka.getObjem());
}
catch(NumberFormatException ex) {
System.out.println("Nezadali ste číslo!");
System.out.println(ex.getMessage());
}
}
else if(menukocka == 3) {
System.out.println("Zadajte povrch");
try {
povrch = Double.parseDouble(in.nextLine());
Tvary3D kocka = new Kocka();
kocka.setPovrch(povrch);
System.out.println("Strana a je: " + kocka.getStranuA());
System.out.println("Uhlopriečka je: " + kocka.getUhlopriecka());
System.out.println("Povrch je: " + kocka.getPovrch());
System.out.println("Objem je: " + kocka.getObjem());
}
catch(NumberFormatException ex) {
System.out.println("Nezadali ste číslo!");
System.out.println(ex.getMessage());
}
}
}
else
System.out.println("Neplatný výber!");
}
}
else
System.out.println("Neplatný výber!");
}
}
.
package vypoctytelies;
public abstract class Tvary3D {
protected double objem, povrch;
public double getObjem() {
return objem;
}
public void setObjem(double obj) {
objem = obj;
}
public double getPovrch() {
return povrch;
}
public void setPovrch(double povr) {
povrch = povr;
}
abstract double getUhlopriecka();
abstract double getStranuA();
}
.
package vypoctytelies;
public class Kocka extends Tvary3D {
private double a;
public Kocka(double sA) {
a = sA;
}
public Kocka() {
}
@Override
public double getPovrch() {
return (6 * a * a);
}
@Override
public double getObjem() {
return (a * a * a);
}
@Override
public double getUhlopriecka() {
return (a * Math.sqrt(2));
}
@Override
public double getStranuA() {
return a;
}
}
Jo, to už je použítí OOP v praxi.Používáš dědičnost a zapouzdření a nepředvádíš tvz. "programování s objekty". K plnému OOP už jen chybí dodělat polymorfismus(přes dědičnost nebo rozhraní).
mayo505:13.3.2016 17:42
Je to lepšie ale stále mi to nedáva na 100% zmysel. Keď máš abstraktnú triedu Tvary3D (mimochodom, mala by sa volať Tvar3D), očakávaš, že každý jej potomok bude použiteľný namiesto nej. To ale nie je úplne pravda. Čo ak budeš mať nejaký superdivný 3D tvar, vytvoríš z neho objekt a u neho nemajú zmysel funkcie ako getUhlopriecka(), getStranuA(). Alebo napríklad taký štvorsten, ten asi ani nemá uhlopriečku (aj ak má tak je to strana).
Podľa mňa ti dedičnosť neprináša žiadne výhody (teda okrem toho, že nemusíš písať funkciu setObjem a setPovrch znova ale to je zlé použitie dedičnosti) a podľa mňa tu ani nemá význam (resp. nie takto).
Robertov komentár tiež nedáva zmysel, zapúzdrenie tu skoro ani nemáš, keďže môžeš kedykoľvek zmeniť objem. Keď potrebuješ vytvárať objekty z objemu a povrchu (čo je samo o sebe divné a v minime 3D tvarov to má význam) radšej by som to robil nejakou factory metodou napr.
Kocka kocka = Kocka.vytvorZObjemu(objem)
Takisto neplatí, že ak použiješ dedičnosť, zapúzdrenie a polymorfizmus hneď máš super OOP a keď nie tak nie (mimochodom povedal by som, že aký taký polymorfizmus v tomto prípade existuje).
"Robertov komentár tiež nedáva zmysel, zapúzdrenie tu skoro ani nemáš, keďže môžeš kedykoľvek zmeniť objem. Keď potrebuješ vytvárať objekty z objemu a povrchu (čo je samo o sebe divné a v minime 3D tvarov to má význam) radšej by som to robil nejakou factory metodou napr."
A co je tohle?
public class Kocka extends Tvary3D {
private double a;
zapouzdření není jenom o použití "private". I když je pravda že pokud je proměnná "protected" a třídy jsou ve stejném balíčku tak tím nic nezapouzdří.
public abstract class Tvary3D {
protected double objem, povrch;
Pointou je pochopení principů OOP, nikoliv provádění OOA či OOD čehož je důkazem i název tématu.
mayo505:14.3.2016 12:32
Napísal som "skoro", nie, že "vôbec". A ako vravíš zapúzdrenie nie je len o používaní private. Síce má "zapúzdrený" atribút a, na druhej strane ale umožňuje nastaviť a zmeniť atribút objem. Príklad
Kocka kocka = new Kocka(5);
kocka.setObjem(1000) // v tomto momente a = 5, objem = 1000
Konkrétne v tomto prípade u kocky je povedzme to zapúzdrenie ako tak dodržané, na vonok sa tvári konzistentne (aj keď vo vnútri nie je) a to preto lebo prepísal funkciu getObjem(), tak sa ospravedlňujem za silnejšie slová. Ale ak vytvorí novú triedu tam to už nemusí dodržať.
Mimochodom nechápem ako môže tento kód fungovať
objem = Double.parseDouble(in.nextLine());
Tvary3D kocka = new Kocka();
kocka.setObjem(objem);
System.out.println("Strana a je: " + kocka.getStranuA());
System.out.println("Uhlopriečka je: " + kocka.getUhlopriecka());
System.out.println("Povrch je: " + kocka.getPovrch());
System.out.println("Objem je: " + kocka.getObjem());
Keď všetky tie funkcie u kocky pracujú s atribútom "a", ktorý nie je nikde nastavený
coells:14.3.2016 13:08
Máš pravdu a zapouzdření tam vůbec není, žádné skoro.
Je to typická ukázka "programování s objekty".
Pod to, co jsi psal včera, bych se klidně podepsal s výjimkou factory
metod.
To je obezlička v Javě, která řešit nepříjemnosti s chybějícím
polymorfismem inicializérů.
mayo505:14.3.2016 13:44
dik ... ako by potom konkrétne vyzeralo to vytvorenie objektu z objemu? Nejaký stručný príklad
coells:14.3.2016 18:20
V Javě tak, jak jsi napsal.
V dynamických jazycích mám radši class clusters než factory, kód je
přirozenější a umožňuje polymorfismus.
V OOP bych se zamyslel nad tím, že míra a povrch mohou být také objekty,
ale na takových vyumělkovaných příkladech se cokoliv špatně
vymýšlí.
Zobrazeno 14 zpráv z 14.