PDA

Volledige versie bekijken : Errors like Events


theFlashWizard
%Europe/Berlin %697 %2007, 17:44
Errors like Events
Beste developers,
Toen ik het exceptions gedeelte van EAS3 (Essential ActionScript 3.0) doorlas had ik een idee. Nu heb ik wat tijd gehad om deze klein uit te werken en ben ik benieuwd wat jullie van het idee vinden.
Voor informatie.
Essential ActionScript 3.0 beschrijft 3 opties voor het beheren van meerdere errors, opties op basis van precisie (engels: granularity):

Één error subclass om het probleem te beschrijven.
Je kan maar 1 Error onderscheiden.
Één error subclass om het probleem te beschrijven, maar met message meegeef functie.
Hierbij kan je de errors onderscheiden door deze message.
Een error subclass per Error.
Je kan verschillende catch's gebruiken waarbij elke een bepaalde subclass opvangt. Op die manier heb je ook je onderscheiding.


Optie 2 lijkt het makkelijkste, maar omdat je snel typefouten maakt in strings lijkt dit geen nette oplossing.
Optie 3 lijkt het netst, maar een class per error vind ik persoonlijk wat overdreven.

Het idee
Mijn idee was om optie 2 te gebruiken, maar dan constants te gebruiken om errors te onderscheiden (Net zoals bij events).
Niet heel geavanceerd, maar net een hele mooie middenweg tussen optie 2 en 3, tenminste volgens mij dan.

Voorbeeld
Een simpel voorbeeld van mijn idee. Inderdaad het "set pet name" voorbeeld heb ik ook uit EAS3.
PetError class
De Error subclass die de type events beschrijft in constants. (Net zoals MouseEvent bijv.)
package {
public class PetError extends Error{

static public const NAME_TO_LONG:String = "nameToLong";
static public const NAME_TO_SHORT:String = "nameToShort";
static public const NO_NAME:String = "noName";

public function PetError(message:String):void{
super(message);
}
}
}
Pet class
De Pet class met een name property die wanneer nodig een error throws.
package {
public class Pet{

static private var maxNameLength:Number = 5;
static private var minNameLength:Number = 2;

private var _name:String;

public function Pet():void{

}

public function get name():String{
return this._name;
}
public function set name(newName:String):void{
if(newName == ""){
throw new PetError(PetError.NO_NAME);
}else if(newName.length > Pet.maxNameLength){
throw new PetError(PetError.NAME_TO_LONG);
}else if(newName.length < Pet.minNameLength){
throw new PetError(PetError.NAME_TO_SHORT);
}
this._name = newName;
}
}
}
ErrorHandling class
De ErrorHandling class die de pet maakt en hem probeert verschillende namen te geven.
(Eigenlijk hoort er een algemene Error catch bij, maar deze heb ik voor eenvoudigheid weggelaten)
package {
import flash.display.Sprite;

public class ErrorHandling extends Sprite{

private var _pet:Pet

public function ErrorHandling():void{
addPet();
setPetName("Mister Lion");
setPetName("Lion");
setPetName("L");
setPetName("");
}
public function addPet(){
this._pet = new Pet();
}
private function setPetName(petName:String):void{
trace("")
trace("setPetName: "+petName)
try{
this._pet.name = petName;
}catch(error:PetError){
trace("An Pet error occorred:");
switch (error.message){
case PetError.NO_NAME:
trace(" No name specified.");
break;

case PetError.NAME_TO_LONG:
trace(" The name is too long.");
break;

case PetError.NAME_TO_SHORT:
trace(" The name is too short.");
break;
}
}
trace("New pet name: "+this._pet.name);
}
}
}
Output
Het resultaat van alle traces:
setPetName: Mister Lion
An Pet error occorred:
The name is too long.
New pet name: null

setPetName: Lion
New pet name: Lion

setPetName: L
An Pet error occorred:
The name is too short.
New pet name: Lion

setPetName:
An Pet error occorred:
No name specified.
New pet name: Lion

Description
In dit systeem zou je ook makkelijk een description systeem kunnen maken.
Weer een klein voorbeeld:
PetError class
package {
public class PetError extends Error{

static public const NAME_TO_LONG:String = "nameToLong";
static public const NAME_TO_SHORT:String = "nameToShort";
static public const NO_NAME:String = "noName";

public var description:String;

public function PetError(message:String):void{
switch (message){
case PetError.NO_NAME:
description = "No name specified.";
break;

case PetError.NAME_TO_LONG:
description = "The name is too long.";
break;

case PetError.NAME_TO_SHORT:
description = "The name is too short.";
break;
}
super(message);
}
}
}
ErrorHandling class
(Eigenlijk is de switch, in mijn voorbeeld, overbodig, maar je wilt niet voor niks errors onderscheiden, dus heb ik het zo gelaten.)
package {
import flash.display.Sprite;

public class ErrorHandling extends Sprite{

private var _pet:Pet

public function ErrorHandling():void{
addPet();
setPetName("Mister Lion");
setPetName("Lion");
setPetName("L");
setPetName("");
}
public function addPet(){
this._pet = new Pet();
}
private function setPetName(petName:String):void{
trace("")
trace("setPetName: "+petName)
try{
this._pet.name = petName;
}catch(error:PetError){
trace("An Pet error occorred:");
switch (error.message){
case PetError.NO_NAME:
trace(" "+error.description);
break;

case PetError.NAME_TO_LONG:
trace(" "+error.description);
break;

case PetError.NAME_TO_SHORT:
trace(" "+error.description);
break;
}
}
trace("New pet name: "+this._pet.name);
}
}
}
Hierdoor heb je als maker van de Pet class de mogelijkheid kant en klare descriptions mee te leveren.

Dus?
Dus wat vinden jullie van deze ideeën?

Alvast bedankt voor jullie mening

M0L
%Europe/Berlin %778 %2007, 19:41
Ziet goed uit en dit is ook zeer belangrijk om de gebruiken, want als je de foutafhandeling niet scheidt van de rest van de code dan wordt je code onleesbaar en dan krijg je alleen maar meer fouten. Optie 3 vind ik ook wat overdreven.

Is het niet handiger om de descriptions in de PetError class te zetten ?

EDIT: ik moet de volgende keer wat beter kijken

Dauntless
%Europe/Berlin %977 %2007, 00:27
Lijkt me vrij logisch om het zo op te lossen :). Eigenlijk dom dat ze eigenlijk de techniek van de events niet toepassen op de errors.

TheDutch
%Europe/Berlin %378 %2007, 10:05
@TFW: Optie 2 is in de praktijk ook de meest gebruikte of het nu met constants gedaan wordt of niet. Persoonlijk maak ik ook zoveel als mogelijk gebruik van constants voor statische waarden die op meerdere plekken gebruikt kunnen worden. Hierdoor bescherm je de onderhoudbaarheid van je code en is het makkelijker te hergebruiken. Al met al een prima constatering dus! :).

@Dauntless: Kan je eens toelichten waarom het in jouw ogen "dom" is dat Adobe naar jouw mening event type onderscheiding niet heeft toegepast op errors. Volgensmij heeft Adobe juist die deur heel goed voor ons opengehouden. De standaard errors geven een enkel bericht dat aangeeft dat de betreffende error zich heeft voorgedaan. Prima! Jij kunt zelf een class met constants aanmaken met berichten en/of codes als waarde en die meegeven als de argumenten "message" en "id" (in standaard error subclasses niet altijd zichtbaar in code completion maar werkt wel) aan de constructor van de specifieke error class en de onderscheiding vervolgens maken op het "id" (terug te lezen in Error object als "errorID") argument. Je kunt ook een subclass maken van een error (sub)class en daarin de constants verwerken zoals TFW gedaan heeft. Ik zie niet in wat hier aan Adobe's kant verkeerd aan is gedaan.

VOORBEELD:

public static const NO_NAME:int = 5;

try
{
// PetError(message:String, id:int=0);
throw new PetError("No name specified.", NO_NAME);
}
catch(e:PetError)
{
trace("Message: "+e.message); // Message: No name specified.
trace("ID: "+e.errorID); // ID: 5
}

Dauntless
%Europe/Berlin %417 %2007, 11:01
Bv omdat je classes als 'ArgumentError' ook gemakkelijk in je eigen classes zou kunnen gebruiken zonder dat je hem zelf opnieuw moet maken. Het zou dan handig zijn als er toch een soort standaard manier is om deze dan ook te gebruiken in je eigen classes.

Als je bv een static method in de ArgumentError class maakt, dan kan die class automatisch met arguments.callee een error message opmaken waarin de classenaam e.d. ingezet wordt.

TheDutch
%Europe/Berlin %423 %2007, 11:10
Misschien moet je toch wat duidelijker zijn want ik volg je niet helemaal :).

Je zou ArgumentError zo kunnen gebruiken in je eigen class:

private function mijnFunctie(...args):void
{
if(args.length < 2)
{
throw new ArgumentError("Er zijn te weinig argumenten meegegeven.");
}
}


mijnFunctie(1);

TheDutch
%Europe/Berlin %433 %2007, 11:24
Als je bv een static method in de ArgumentError class maakt, dan kan die class automatisch met arguments.callee een error message opmaken waarin de classenaam e.d. ingezet wordt.
Je kunt niet zomaar methods toevoegen aan een class at runtime. Je dient die class dan te extenden om zo'n method toe te voegen. Dat is OO logica natuurlijk :).

Wanneer jij dynamisch een message wil opmaken dan kan je daar zelf een seperate ArgumentErrorMessages class voor maken, die kan dan alle opmaken van messages afhandelen. Wat jij wilt dat standaard in de ArgumentError class zit gaat zijn initieel doel voorbij en is iets specifieks wat jij zelf moet maken.

ps. Je hebt "arguments" niet altijd beschikbaar zoals wanneer je ...(rest) keyword gebruikt.
ps2(edit). Dat "arguments" dus met ...(rest) niet beschikbaar is maakt helemaal nets uit omdat met ...(rest) je nooit een ArgumentError nodig hebt ;).

Dauntless
%Europe/Berlin %443 %2007, 11:38
Ja, sorry, 'k kom vandaag niet goed uit m'n woorden ... :p Vergat dat ik iets zei :).

TheDutch
%Europe/Berlin %447 %2007, 11:43
Proberen we het morgen weer :P.
Ik ga maar eens wat doen vandaag, al bijna 12 uur! :)

theFlashWizard
%Europe/Berlin %678 %2007, 17:16
Had jullie posts helemaal gemist, dus excuses voor mijn late respons.

De deur openhouden betekend een boel vrijheid. Dit is natuurlijk niet altijd goed omdat niet iedereen vergevorderd is in OOD. Wanneer je mensen iets minder vrij laad kun je ze de goede richting op wijzen.

Daarnaast vind ik het systeem wat inconsequent omdat het de enigste is in ActionScript dat onderscheid op datatype/parameter. Ik vind het lijken op java, daarin kun je alle functies vaker beschrijven met verschillende parameters. Zo wordt een functie gekozen aan de hand van de parameters. Maar omdat het in ActionScript de enigste is die dit doet vind ik het wat verwarrend.

Om de huidige, overzichtelijke onderverdeling in de built-in Errors te houden zou ik misschien wel een categorie property maken. (Al zou je ook met de is operator classes uit elkaar kunnen houden).
Waarom zou je aan deze Error classes geen constants verzameling toevoegen? Die specifieke errors specifieker beschrijven? Zo heb je meteen een goed voorbeeld hoe men custom error subclasses zou kunnen maken.

Ik begrijp dat ActionScript echter ook de ECMA standaard wil volgen en dat dit een beslissing is die hun dan niet alleen maken.

Ben het met theDutch eens dat je Error classes moet extenden om specifieke dingen als messages toe te voegen.

Flash Monk-ey!
%Europe/Berlin %836 %2007, 21:03
Op ditzelfde gebied heb ik laatst 2 classjes geschreven die naar mijn mening wel handig zijn.
De eerste is de ErrorDispatcher, die probeert het gedrag van flash zelf te imiteren. Daarmee bedoel ik dat hij eerst op zoek gaat naar een listener voor het "error" event. Als hij die niet kan vinden throwt hij het error object alsnog. Je gebruikt de throwError functie op dezelfde plaats als waar je normaal gesproken "throw new Error(bla bla) zou neerzetten
De tweede is het ErrEvent. In dat event zit het error object in een property. Daardoor kan je altijd bij alle properties van error object, en hoef je niet voor elk error type een event type te maken.

Tips/aanvullingen/commentaar etc is zeer welkom

De classes:


public class ErrorDispatcher implements IErrorDispatcher {

private var target:IErrorDispatcher;

public function ErrorDispatcher(_target:IErrorDispatcher = this) {
this.target = _target;
}

public function throwError(_error:Error):void {
if(this.target.hasEventListener(ErrEvent.ERROR)) {
this.target.dispatchEvent(new ErrEvent(ErrEvent.ERROR, _error, false, false));
} else {
throw error;
}
}
}

//De interface die erbij hoort:
interface IErrorDispatcher extends IEventDispatcher {
public function throwError(error:Error):void
}

//tweede class:
public class ErrEvent extends Event {

public static const ERROR:String = "error";

public var error:Error;

public function ErrEvent(_type:String, _error:Error, _bubbles:Boolean = false, _cancelable:Boolean = false) {

super(_type, _bubbles, _cancelable);

this.error = _error;
}
}