mboost-dp1
OO fun
- Forside
- ⟨
- Forum
- ⟨
- Tagwall
Her er et meget simpelt (?) Java program.
Hvad udskriver det?
Hvad udskriver det?
public class OOFun {
public static class A {
public void m(int v) {
System.out.println(v);
}
public void m(int u, int v) {
System.out.println(u + " " + v);
}
}
public static class B extends A {
public void m(int v) {
m(0, v);
}
}
public static class C extends B {
public void m(int u, int v) {
super.m(u + 1, v);
}
}
public static void main(String[] args) {
A o = new C();
o.m(2);
}
}
#1
Hvis folk foretrækker C#:
Hvis folk foretrækker C#:
using System;
public class OOFun
{
public class A
{
public virtual void M(int v)
{
Console.WriteLine(v);
}
public virtual void M(int u, int v)
{
Console.WriteLine(u + " " + v);
}
}
public class B : A
{
public override void M(int v)
{
M(0, v);
}
}
public class C : B
{
public override void M(int u, int v)
{
base.M(u + 1, v);
}
}
public static void Main(string[] args) {
A o = new C();
o.M(2);
}
}
Det burde vel udskrive "0 2"
Medmindre B.m(v) kalder C.m(u, v) i stedet for A.m(u, v), hvor det i så fald ender med at udskrive "1 2"
Medmindre B.m(v) kalder C.m(u, v) i stedet for A.m(u, v), hvor det i så fald ender med at udskrive "1 2"
#8
Ja.
Men det er faktisk et eksempel som jeg har brugt.
Naturligvis ikke i den form.
En lille database loader.
A.m(v) gemmer i databasen med database auto generate keys
A.m(u,v) gemmer i databasen med explicit keys
B.m(v) genererer keys i applikationen og kalder A.m(u,v) eller C.m(u,v)
C.m(u,v) putter data i kø og en anden tråd henter fra kø og kalder A.m(u,v)
Ja.
Men det er faktisk et eksempel som jeg har brugt.
Naturligvis ikke i den form.
En lille database loader.
A.m(v) gemmer i databasen med database auto generate keys
A.m(u,v) gemmer i databasen med explicit keys
B.m(v) genererer keys i applikationen og kalder A.m(u,v) eller C.m(u,v)
C.m(u,v) putter data i kø og en anden tråd henter fra kø og kalder A.m(u,v)
#1 #2, ja den kode er til at blive rundtosset af.
Jeg har erfaret at encapsulation er et af de vigtigste principper for at skrive god kode. Hver modul, klasse, funktion osv. bør have så lille og veldefineret en grænseflade som mulig med resten af koden.
Når man nedarver er grænsefladen mellem baseklassen og nedarveren kompliceret og uigennemskuelig, og tilbøjelig til at gå i stykker ved kodeændringer.
Jeg har aldrig været fan af java's designfilosofi med at have en milliard bittesmå klasser der nedarver hinanden i en stor og filtret træstruktur. Jeg føler ikke det er en effektiv og vedligeholdelsesvenlig måde at skrive kode på.
Abstrakte interfaces er derimod en fantastisk god idé, der netop definerer en grænseflade helt klart.
Jeg har erfaret at encapsulation er et af de vigtigste principper for at skrive god kode. Hver modul, klasse, funktion osv. bør have så lille og veldefineret en grænseflade som mulig med resten af koden.
Når man nedarver er grænsefladen mellem baseklassen og nedarveren kompliceret og uigennemskuelig, og tilbøjelig til at gå i stykker ved kodeændringer.
Jeg har aldrig været fan af java's designfilosofi med at have en milliard bittesmå klasser der nedarver hinanden i en stor og filtret træstruktur. Jeg føler ikke det er en effektiv og vedligeholdelsesvenlig måde at skrive kode på.
Abstrakte interfaces er derimod en fantastisk god idé, der netop definerer en grænseflade helt klart.
arne_v (9) skrev:En lille database loader.
A.m(v) gemmer i databasen med database auto generate keys
A.m(u,v) gemmer i databasen med explicit keys
B.m(v) genererer keys i applikationen og kalder A.m(u,v) eller C.m(u,v)
C.m(u,v) putter data i kø og en anden tråd henter fra kø og kalder A.m(u,v)
Hvorfor er A, B og C overhovedet forbundne med nedarvning? Det forekommer mig at være væsentligt forskellige ting der foregår, A er database tilgang, B har noget med application at gøre, C håndterer kø og multithreading. A, B og C bør være helt separate "ting".
larsp (11) skrev:
#1 #2, ja den kode er til at blive rundtosset af.
Ja. På trods af at den umiddelbart virke rmeget simpel.
larsp (11) skrev:
Jeg har erfaret at encapsulation er et af de vigtigste principper for at skrive god kode. Hver modul, klasse, funktion osv. bør have så lille og veldefineret en grænseflade som mulig med resten af koden.
Jep.
larsp (11) skrev:
Når man nedarver er grænsefladen mellem baseklassen og nedarveren kompliceret og uigennemskuelig, og tilbøjelig til at gå i stykker ved kodeændringer.
Jeg ved ikke helt om jeg vil kalde det en grænseflade. Men det kan ihvertfald nemt blive uigennemskueligt. Det er svært at arve uden at være afhængig af implementationen.
Joshua Bloch har et klassisk eksempel i Effective Java i afsnittet Favor composition over inheritance.
larsp (11) skrev:
Jeg har aldrig været fan af java's designfilosofi med at have en milliard bittesmå klasser der nedarver hinanden i en stor og filtret træstruktur. Jeg føler ikke det er en effektiv og vedligeholdelsesvenlig måde at skrive kode på.
Hvor meget der er af implementations arv varierer mellem forskellige dele af Java. Swing har meget af det. Men andre dele har stort set intet.
Og jeg tror ikke at Java klasser er mindre en klasser i andre sprog. Men Java forventer at public klasser er i en separat fil, hvilket giver rigtigt mange små filer.
larsp (11) skrev:
Abstrakte interfaces er derimod en fantastisk god idé, der netop definerer en grænseflade helt klart.
Jep.
Og Gosling er helt enig.
arne_v (15) skrev:
Men Java forventer at public klasser er i en separat fil, hvilket giver rigtigt mange små filer.
For en god ordens skyld.
Java som i Java Language Specification siger kun:
If and only if packages are stored in a file system (§7.2), the host system may
choose to enforce the restriction that it is a compile-time error if a class or interface
is not found in a file under a name composed of the class or interface name plus an
extension (such as .java or .jav) if either of the following is true:
...
• The class or interface is declared public (and therefore is potentially accessible
from code in other packages).
Men alle nyere Java implementationer (SUN/Oracle, OpenJDK, IBM, HP etc.) gemmer source code i fil system og enforcer den regel om public klasser i separate filer.
#17
Og hvis nogen undrer sig over det med fil system.
JLS tillader eksplicit at gemme Java source code i en database.
For ca. 20 år siden havde jeg den tvivlsomme fornøjelse at arbejde med en sådan implementation.
Klasser som rækker i en tabel. Members af klasser som rækker i en anden tabel med en FK til den første tabel. Man valgte først en klasse og så et member, editerede og gemte member. Skulle man se hele klassen var det eksport til report!
Det koncept blev hurtigt droppet!!
Og hvis nogen undrer sig over det med fil system.
JLS tillader eksplicit at gemme Java source code i en database.
For ca. 20 år siden havde jeg den tvivlsomme fornøjelse at arbejde med en sådan implementation.
Klasser som rækker i en tabel. Members af klasser som rækker i en anden tabel med en FK til den første tabel. Man valgte først en klasse og så et member, editerede og gemte member. Skulle man se hele klassen var det eksport til report!
Det koncept blev hurtigt droppet!!
#17 Wow, det lyder som hel.. på jord, jeg mener en rigtig enterprisey idé....
Med den rette IDE kunne det måske bringes til at virke. Men en noget misforstået idé, for et filsystem er jo allerede en database som er født med en hierarkisk struktur der giver god mening til kodefiler.
Med den rette IDE kunne det måske bringes til at virke. Men en noget misforstået idé, for et filsystem er jo allerede en database som er født med en hierarkisk struktur der giver god mening til kodefiler.
arne_v (15) skrev:larsp (11) skrev:
Når man nedarver er grænsefladen mellem baseklassen og nedarveren kompliceret og uigennemskuelig, og tilbøjelig til at gå i stykker ved kodeændringer.
Jeg ved ikke helt om jeg vil kalde det en grænseflade.
Det er vel en grænseflade i den sproglige forstand. Én klasse klistrer sig op af en anden klasse. Alt der hvor der "klistres" er vel en grænseflade mellem to kode moduler / klasser.
arne_v (15) skrev:
Det er svært at arve uden at være afhængig af implementationen.
Joshua Bloch har et klassisk eksempel i Effective Java i afsnittet Favor composition over inheritance.
Måske er det værd lige at forklare eksemplet.
Hans eksempel bruger HashSet<> men det er nemmere med en container klasse som indeholder 3 int.
Interface:
public interface ThreeInt {
public void setOne(int ix, int v);
public void setAll(int[] v);
public int[] getData();
public String getId();
// +100 more methods
}
Ønske om at tælle antal kald til setOne og setAll.
Det virker oplagt at arve fra implementations klassen og så override de 2 metoder og tælle op der.
Resultatet er imidlertid aldeles upålideligt.
public class Test {
public static interface InstrumentedThreeInt extends ThreeInt {
public int getSetOneCalls();
public int getSetAllCalls();
}
public static class InstrumentedThreeIntA extends ThreeIntA implements InstrumentedThreeInt {
private int soc = 0;
private int sac = 0;
public void setOne(int ix, int v) {
soc++;
super.setOne(ix, v);
}
public void setAll(int[] v) {
sac++;
super.setAll(v);
}
public int getSetOneCalls() {
return soc;
}
public int getSetAllCalls() {
return sac;
}
}
public static class InstrumentedThreeIntB extends ThreeIntB implements InstrumentedThreeInt {
private int soc = 0;
private int sac = 0;
public void setOne(int ix, int v) {
soc++;
super.setOne(ix, v);
}
public void setAll(int[] v) {
sac++;
super.setAll(v);
}
public int getSetOneCalls() {
return soc;
}
public int getSetAllCalls() {
return sac;
}
}
public static void main(String[] args) {
InstrumentedThreeInt a = new InstrumentedThreeIntA();
a.setAll(new int[] { 1, 2, 3});
a.setOne(1, -2);
System.out.printf("%s : %d %d\n", a.getId(), a.getSetOneCalls(), a.getSetAllCalls());
InstrumentedThreeInt b = new InstrumentedThreeIntB();
b.setAll(new int[] { 1, 2, 3});
b.setOne(1, -2);
System.out.printf("%s : %d %d\n", b.getId(), b.getSetOneCalls(), b.getSetAllCalls());
}
}
udskriver:
A : 1 1
B : 4 1
Fordi ThreeIntA.setAll ser ud som:
public void setAll(int[] v) {
for(int i = 0; i < 3; i++) {
data[i] = v[i];
}
}
og ThreeIntB.setAll ser ud som:
public void setAll(int[] v) {
for(int i = 0; i < 3; i++) {
setOne(i, v[i]);
}
}
Valget af arv og override gør at resultatet afhænger af implementeringen og dermed bryder med indkapslingen.
Det virker fint med delegering:
public class Test2 {
public static interface InstrumentedThreeInt extends ThreeInt {
public int getSetOneCalls();
public int getSetAllCalls();
}
public static class InstrumentedThreeIntX implements InstrumentedThreeInt {
private int soc = 0;
private int sac = 0;
private ThreeInt real;
public InstrumentedThreeIntX(ThreeInt real) {
this.real = real;
}
public void setOne(int ix, int v) {
soc++;
real.setOne(ix, v);
}
public void setAll(int[] v) {
sac++;
real.setAll(v);
}
public int[] getData() {
return real.getData();
}
public String getId() {
return real.getId();
}
// +100 more methods
public int getSetOneCalls() {
return soc;
}
public int getSetAllCalls() {
return sac;
}
}
public static void main(String[] args) {
InstrumentedThreeInt a = new InstrumentedThreeIntX(new ThreeIntA());
a.setAll(new int[] { 1, 2, 3});
a.setOne(1, -2);
System.out.printf("%s : %d %d\n", a.getId(), a.getSetOneCalls(), a.getSetAllCalls());
InstrumentedThreeInt b = new InstrumentedThreeIntX(new ThreeIntB());
b.setAll(new int[] { 1, 2, 3});
b.setOne(1, -2);
System.out.printf("%s : %d %d\n", b.getId(), b.getSetOneCalls(), b.getSetAllCalls());
}
}
udskriver:
A : 1 1
B : 1 1
Imidlertid skal der laves en masse trivielle delegerende metoder.
Kotlin kan gøre det smartere:
interface InstrumentedThreeInt : ThreeInt {
fun getSetOneCalls(): Int
fun getSetAllCalls(): Int
}
class InstrumentedThreeIntX(val real: ThreeInt) : ThreeInt by real, InstrumentedThreeInt {
var soc: Int = 0
var sac: Int = 0
override fun setOne(ix: Int, v: Int): Unit {
soc++
real.setOne(ix, v)
}
override fun setAll(v: IntArray): Unit {
sac++
real.setAll(v)
}
override fun getSetOneCalls(): Int = soc
override fun getSetAllCalls(): Int = sac
}
fun main(): Unit {
val a = InstrumentedThreeIntX(ThreeIntA())
a.setAll(intArrayOf(1, 2, 3))
a.setOne(1, -2)
println("%s : %d % d".format(a.getId(), a.getSetOneCalls(), a.getSetAllCalls()))
val b = InstrumentedThreeIntX(ThreeIntB())
b.setAll(intArrayOf(1, 2, 3))
b.setOne(1, -2)
println("%s : %d % d".format(b.getId(), b.getSetOneCalls(), b.getSetAllCalls()))
}
Opret dig som bruger i dag
Det er gratis, og du binder dig ikke til noget.
Når du er oprettet som bruger, får du adgang til en lang række af sidens andre muligheder, såsom at udforme siden efter eget ønske og deltage i diskussionerne.