2012-10-16, 18:27
  #1
Medlem
Luciditys avatar
Kom inte på nåt bra topicnamn men låt oss säga att vi har den här koden.

[PHP]void function() {

doThing1();
doThing2();
doThing3();
doThing4();
doThing5();

}[/PHP]
Dessa metoder försöker göra något, och om de failar vill jag hoppa ur funktionerna och återställa de som tidigare körts. Om doThing3(); failar vill jag alltså inte köra vidare utan istället köra undoThing2(); samt undoThing1();. Hur designar man detta på ett vettigt sätt?

Såhär är det enda jag kan tänka mig:
[PHP]void function() {

doThing1();
if (failed) {
return;
}
doThing2();
if (failed) {
undoThing1();
return;
}
doThing3();
if (failed) {
undoThing1();
undoThing2();
return;
}
doThing4();
if (failed) {
undoThing1();
undoThing2();
undoThing3();
return;
}
doThing5();
if (failed) {
undoThing1();
undoThing2();
undoThing3();
undoThing4();
return;
}
everythingWentFine();

}[/PHP]

Men detta ser väldigt fult ut och skulle bli exponentiellt fulare ju mer kollar man behöver göra.
Citera
2012-10-16, 19:51
  #2
Medlem
Jooncs avatar
Exempel:
Skapa en superklass eller interface med metoderna do() och undo(). Döp den tex till Action. Låt varje grej du vill göra ärva/implementera denna klassen/interfacet.
Skapa en stack av typen Action. Efter varje lyckad exekvering av do() på en Action pushar du den till stacken (stack.push(action)). Om exekveringen av en action misslyckas (tex returnerar false) så kör du:
[PHP](while !stack.isEmpty()){
Action action = stack.pop();
action.undo();
}[/PHP]
Citera
2012-10-16, 20:09
  #3
Medlem
Luciditys avatar
Citat:
Ursprungligen postat av Joonc
Exempel:
Skapa en superklass eller interface med metoderna do() och undo(). Döp den tex till Action. Låt varje grej du vill göra ärva/implementera denna klassen/interfacet.
Skapa en stack av typen Action. Efter varje lyckad exekvering av do() på en Action pushar du den till stacken (stack.push(action)). Om exekveringen av en action misslyckas (tex returnerar false) så kör du:
[PHP](while !stack.isEmpty()){
Action action = stack.pop();
action.undo();
}[/PHP]
Det låter som ett betydligt bättre sätt. Här får alltså mina doThing-metoder varsin egen klass? Jag brukar vara lite sparsam med antal klasser men det är väl egentligen ingen nackdel att bara slänga in alla i ett eget paket.
Citera
2012-10-17, 14:08
  #4
Medlem
Vad sägs om att bara klona objektet först och om funktionen lyckas returnerar man objektet annars kopian ?

Typ
Kod:
Object doStuff(Object o){
    Object copy = o.clone(); // Eller hur man nu kopierar ett objekt
    boolean success = false;
    success = doStuff1(o);
    success = doStuff2(o);
    success = doStuff3(o);
    if (success) return o else return copy;
}
boolean doStuff1(Object o){
    return // Nått
}

typ ?

Kanske inte det bästa ur prestanda och minnes synpunkt (massa onödiga kopieringar) men lätt att förstå principen.
Citera
2012-10-17, 20:22
  #5
Medlem
Luciditys avatar
Citat:
Ursprungligen postat av SixtenSune
Vad sägs om att bara klona objektet först och om funktionen lyckas returnerar man objektet annars kopian ?

Typ
Kod:
Object doStuff(Object o){
    Object copy = o.clone(); // Eller hur man nu kopierar ett objekt
    boolean success = false;
    success = doStuff1(o);
    success = doStuff2(o);
    success = doStuff3(o);
    if (success) return o else return copy;
}
boolean doStuff1(Object o){
    return // Nått
}

typ ?

Kanske inte det bästa ur prestanda och minnes synpunkt (massa onödiga kopieringar) men lätt att förstå principen.
Det där skulle nog funka i många fall men det kan vara ineffektivt som du säger ja. Och jag tycker nog att det ser fult ut.
Citera
2012-10-17, 21:32
  #6
Medlem
Fult vet jag inte. Den koden där är bara hopslängd på 5 sekunder för att visa principen.
Object copy = o.clone();
if (success) return o else return copy;
Det är ju bara 2 raderna som är relevanta men om du tycker det är fult,ok,men jag är inte enig.
Citera
2012-10-17, 22:21
  #7
Medlem
Ursäkta att jag tjatar lite men jag tänkte lite mer på det och måste försvara min kod lite.

Det viktigaste är naturligtvis att veta för och nackdelar med olika lösningar och det finns tillfällen när den inte passar. Men annars är det en enkel och elegant lösning på problemet måste jag säga. Ingen ide att krångla till saker i onödan. Produktiviteten sjunker och risken för bugar ökar.

Och ju fler sätt man lär sig att göra saker på,ju lättare har man att lösa andra liknande problem i framtiden.
Citera
2012-10-17, 22:37
  #8
Medlem
frellis avatar
Citat:
Ursprungligen postat av SixtenSune
Ursäkta att jag tjatar lite men jag tänkte lite mer på det och måste försvara min kod lite.

Det viktigaste är naturligtvis att veta för och nackdelar med olika lösningar och det finns tillfällen när den inte passar. Men annars är det en enkel och elegant lösning på problemet måste jag säga. Ingen ide att krångla till saker i onödan. Produktiviteten sjunker och risken för bugar ökar.

Och ju fler sätt man lär sig att göra saker på,ju lättare har man att lösa andra liknande problem i framtiden.
Ditt sätt var det första som kom upp i mitt huvud när jag läste TS post också. Skulle antagligen också kört på någon typ av kopia att arbeta på. Beror ju dock lite på hur koden ser ut också.
Citera
2012-10-17, 22:41
  #9
Medlem
Hej

Det känns som det finns ett antal vägar att gå lite beroende av vad dina dothing metoder gör

jag ska försöka gå igenom några av dom(missar säkert många)

dothing sparar undan eller ändrar på information i ett perstsitent media typ en databas. då känns det väldigt lämpligt att använda sig av transationer för att rulla tillbaks ändringarna om något går fel(tror java stödjer detta men är inte hundra)

dothing manipulerar en massa instansvariabler, här skulle jag försöka strukturera om koden så att den inte anvönder en massa variabler med olika states. Detta då all min erfarenhet säger att kod som ser ut på det viset blir väldigt svår att underhålla

undantag från förra regeln är om klassen är en nyckelkomponent där prestanda är väldigt vikig eftersom den hanterar väldigt tunga saker/alternativt körs extremt ofta men detta anser jag isf ska vara resultatet av tester som visar att koden behöver optimeras.

Men som sagt mer info om vad din klass ska göra skulle hjälpa
Citera
2012-10-18, 17:54
  #10
Medlem
Jooncs avatar
Jag visar hela exemplet på vad jag menade, jag lägger ofta till undo och redo när jag skriver något program och implementeringen ser alltid ut i stil med följade kod. Jag tror det skulle passa bra in på ditt exempel:
[PHP]
public interface Action {

public void undo();
public void redo();
public void execute();
}
[/PHP]

[PHP]
import java.util.Stack;

public class ActionStack {

private Stack<Action> undoStack;
private Stack<Action> redoStack;

public ActionStack(){
undoStack = new Stack<Action>();
redoStack = new Stack<Action>();
}

public void execute(Action a){
a.execute();
undoStack.push(a);
redoStack.clear();
}

public void undo(){
Action a = undoStack.pop();
a.undo();
redoStack.push(a);
}

public void redo(){
Action a = redoStack.pop();
a.redo();
undoStack.push(a);
}

public boolean canUndo(){
return !undoStack.isEmpty();
}

public boolean canRedo(){
return !redoStack.isEmpty();
}
}
[/PHP]

Följt av 2 exempel på klasser och en körning:

[PHP]
public class AppendStringAction implements Action{

private StringBuilder sb;
private String s;

public AppendStringAction(StringBuilder sb, String s){
this.sb = sb;
this.s = s;
}

@Override
public void undo() {
sb.replace(sb.length()-s.length(), sb.length(), "");
}

@Override
public void redo() {
sb.append(s);
}

@Override
public void execute() {
sb.append(s);
}
}
[/PHP]

[PHP]
public class PrependStringAction implements Action{

private StringBuilder sb;
private String s;

public PrependStringAction(StringBuilder sb, String s){
this.sb = sb;
this.s = s;
}
@Override
public void undo() {
sb.replace(0, s.length(), "");
}

@Override
public void redo() {
sb.insert(0, s);
}

@Override
public void execute() {
sb.insert(0, s);
}
}
[/PHP]

[PHP]
public static void main(String[] args){
ActionStack as = new ActionStack();
System.out.println("Adding:");
StringBuilder sb = new StringBuilder("*");
as.execute(new AppendStringAction(sb, "2"));
as.execute(new PrependStringAction(sb, "3"));
as.execute(new AppendStringAction(sb, "4"));
as.execute(new PrependStringAction(sb, "5"));
as.execute(new AppendStringAction(sb, "6"));
as.execute(new PrependStringAction(sb, "7"));
as.execute(new AppendStringAction(sb, "8"));
as.execute(new PrependStringAction(sb, "9"));
as.execute(new AppendStringAction(sb, "8"));
as.execute(new PrependStringAction(sb, "7"));
as.execute(new AppendStringAction(sb, "6"));
as.execute(new PrependStringAction(sb, "5"));
as.execute(new PrependStringAction(sb, "4"));
as.execute(new AppendStringAction(sb, "3"));
as.execute(new PrependStringAction(sb, "2"));
as.execute(new AppendStringAction(sb, "1"));
as.execute(new PrependStringAction(sb, "0"));
System.out.println(sb);
System.out.println("Undoing:");
while (as.canUndo()){
as.undo();
System.out.println(sb);
}
}
[/PHP]

Output:
HTML-kod:
Adding:
024579753*24688631
Undoing:
24579753*24688631
24579753*2468863
4579753*2468863
4579753*246886
579753*246886
79753*246886
79753*24688
9753*24688
9753*2468
753*2468
753*246
53*246
53*24
3*24
3*2
*2
*
__________________
Senast redigerad av Joonc 2012-10-18 kl. 18:03.
Citera

Skapa ett konto eller logga in för att kommentera

Du måste vara medlem för att kunna kommentera

Skapa ett konto

Det är enkelt att registrera ett nytt konto

Bli medlem

Logga in

Har du redan ett konto? Logga in här

Logga in