webForumDet fria alternativet

Klasser i plural och/eller singular

.NET

43 svar · 1 621 visningar · startad av doggelito · sida 2 av 3

Frågan, av doggelito

En liten fundering bara: Om man ska bygga klasser om t.ex. bilar åt en bilhandlare, hur skulle ni göra då: skapa en klass som heter: "Car" (som innehåller div. egenskaper och metoder om en bil) skulle ni också skapa en som heter "Cars" (som innehåller div. egenskaper och metoder om flera bilar)? jag kommer inte riktigt på vad den undre skulle kunna innehålla för något men är det vettigt att h

Läs frågan i sin helhet →
Medlem sedan juni 20019 024 inlägg
#21

emission skrev:

Nog för att jag en stark tillskyndare av abstraktion, men jag förstod inte riktigt relevansen i det exempel du gav (User/UserEntry). Kan du förklara?

Jag förstår inte riktigt vad du vill veta, men jag menade bara att man arbetar med väldigt simpla metoder som fungerar som gränssnitt till en större logik.

Medlem sedan maj 20012 812 inlägg
#22

Pace skrev:

// Skapa användare.
User user = new UserEntry;

user.Name = "Pace";
user.Email = "anonymous@localhost";
user.Active = True;

// Spara användare.
Users users = new Users;
users.AddUser(user);

Jag förstår inte varför folk envisas med att skapa olika factoryklasser för alla sina objekt.
Du har din entity User och så har en factoryklass users. Det betyder att du skapar 2 klasser för att kunna hantera dina users. Om du nu har 20 entitet klasser så måste du dessutom skapa 20 factoryklaser, det betyder 40 klasser. Och skall man vara riktigt krass så borde du ha 20 Managerklasser som hanterar affärslogiken också, eftersom du inte vill blanda ihop detta med dina factoryklasser, det betyder så fall 60 klasser.

Jag var också inne på detta spår i början och allt skulle var snyggt och väl uppdelat i miljoner med olika lager och klasser. Rent konkret kan man säga att det ballade ut :P

Nu har jag istället mina klasser (entiter) som innehåller data och valideringsregler och andra små finesser samt metadata till OR-mappern. Sedan har jag EN factory (OR-mapper) som hanterar all databaskommunikation. Och sedan beroende på affärslogiken så lägger man den i rena Managerklasser för en entitet (typ en services) eller så ligger den i applikationens businesslager, eller så kan det till och med vara så (förlåt om jag svär) att den finns i min entitetklass (ovanligt, men händer dock).

Ju mer man grottar ner sig i alla de olika pattern som finns destu mer komplex blir koden. Tillslut så tar det längre tid att följa alla snygga pattern och teorier än att skriva själva koden för var programmet skall utföra.

Skriv inte mer kod än vad du behöver är mitt nya motto. Det finns tillfällen när man skall använda sig av alla pattern och uppdelningar i flerskiktade lösningar. Men finns även tillfällen när allt sådant är riktigt overkill och man iprincip skulle kunna göra det "the MS-way" med klicka-gissa-spring. Jag är dock inte riktigt där än (som tur är) men man kommer säkert komma dit en dag...

- M

Medlem sedan juni 20003 076 inlägg
#23

Okej, låt oss säga att man har en klass som heter Car och en som heter Cars.
Car innehåller div. egenskaper och Cars innehåller ett par metoder osv.

Skulle man kunna göra att Car ärver Cars för att slippa instansiera Cars? Eller "gör" man inte så?

Typ

public class Car : Cars
{}

public class Cars
{}
Medlem sedan dec. 19996 721 inlägg
#24

Gladh skrev:

Jag förstår inte varför folk envisas med att skapa olika factoryklasser för alla sina objekt.

Jag håller med. Det finns ett par tillfällen då factoryklasser är intressanta:

1. Ett abstractfactory, dvs. det returnerar olika konkreta typer av en abstrakt klass, beroende på något kontext (konfigurering, argument etc.)
2. Instantieringen av objekten måste bevakas, för loggning, lagring, rättighetskontroll, utsmyckning etc.

I övriga lägen är det mest ett otyg.

Gladh skrev:

Skriv inte mer kod än vad du behöver är mitt nya motto.

Helt rätt.

Gladh skrev:

.... och man iprincip skulle kunna göra det "the MS-way" med klicka-gissa-spring.

Nä, dra en gräns va.... The MS-way är sällan rätt.

Medlem sedan dec. 19996 721 inlägg
#25

doggelito skrev:

Okej, låt oss säga att man har en klass som heter Car och en som heter Cars.
Car innehåller div. egenskaper och Cars innehåller ett par metoder osv.

Skulle man kunna göra att Car ärver Cars för att slippa instansiera Cars? Eller "gör" man inte så?

Njoae... Om den ärvda klassen är specifikt anpassad efter den klass som ärver så känns det som att man skapat en bakvänd och onödig lösning. Du kanske vill exemplifiera mer?

Medlem sedan juni 20003 076 inlägg
#26

emission skrev:

Du kanske vill exemplifiera mer?

Njae, kommer inte på nått exempel för detta.
Jag var mest nyfiken om man brukar låta entitetsklassen (Car) ärva sin factoryklass (kallades den så?) (Cars).
(Om man väljer att inte köra med en ORM)

Jo föresten, min tanke var nog typ så här, om man t.ex. vill uppdatera en bil:

Car car = new Car();
car.Name = "Volvo";
car.Save();

Och metoden Save() ligger ursprungligen i Cars.
Ähh, jag vet inte, börjar snöa in mig känner jag! :OO

Medlem sedan jan. 20012 204 inlägg
#27

doggelito skrev:

Njae, kommer inte på nått exempel för detta.
Jag var mest nyfiken om man brukar låta entitetsklassen (Car) ärva sin factoryklass (kallades den så?) (Cars).
(Om man väljer att inte köra med en ORM)

Jo föresten, min tanke var nog typ så här, om man t.ex. vill uppdatera en bil:

Car car = new Car();
car.Name = "Volvo";
car.Save();

Och metoden Save() ligger ursprungligen i Cars.
Ähh, jag vet inte, börjar snöa in mig känner jag! :OO

Nej man burkar inte låta den ärva.
Det behövs inte, du skickar med enity-klassen in i dina metoder.

Medlem sedan jan. 20012 204 inlägg
#28

Gladh skrev:

Jag förstår inte varför folk envisas med att skapa olika factoryklasser för alla sina objekt.
Du har din entity User och så har en factoryklass users. Det betyder att du skapar 2 klasser för att kunna hantera dina users. Om du nu har 20 entitet klasser så måste du dessutom skapa 20 factoryklaser, det betyder 40 klasser. Och skall man vara riktigt krass så borde du ha 20 Managerklasser som hanterar affärslogiken också, eftersom du inte vill blanda ihop detta med dina factoryklasser, det betyder så fall 60 klasser.

En intressant kommentar! Håller på med ett litet projekt, runt en databas med 10-15 tabeller och det blir en hel del med klasser då jag gör som ovan. I en stor applikation borde det ju bli fruktansvärt mycket klasser (och kod som man tycker gör samma sak).

Vad var det som fick dig att överge konceptet Gladh, ngt mer än att det blev en herrans massa klasser?

Medlem sedan dec. 19996 721 inlägg
#29

doggelito skrev:

Jag var mest nyfiken om man brukar låta entitetsklassen (Car) ärva sin factoryklass (kallades den så?) (Cars).

Nej, det gör man inte, för det resulterar i att Car och Cars sitter ihop ett helt isolerat beroende. Cars är inte relevant utan Car och Car är inte relevant utan Cars (i praktiken är ju Car också Cars). Hela arvet har då spelat ut sin roll och man kan lika gärna göra en enda klass på en gång.

Däremot är det en klart fungerande modell om man gör en mer generell klass för Save-metoderna etc och ärver från den.

public abstract class PersistableClass
{
   public void Save()
   {
      //Kod som sparar objektet i databasen
   }
}

public class Car : PersistableClass
{
   ......
}

public class Boat: PersistableClass
{
   ......
}

Då får man helt plötsligt en nytta, eftersom PersistableClass automatiskt ger både Boat och Car en Save-metod.

I detta läge använder man dessutom med fördel generics. För Save-metoden är det inte så intressant, men väl för metoder som returnerar saker

public abstract class PersistableClass<T,K>
{
   public void Save()
   {
      //Kod som sparar objektet i databasen
   }

   public static T Find(K id)
   {
      ....
   }

   public static List<T> FindAll()
   {
      ....
   }

}

public class Car : PersistableClass<Car,int>
{
   ......
}

public class Boat: PersistableClass<Car,string>
{
   ......
}

...och vips så har man fått en Car.Find(int) och en Boat.Find(string) också....

Detta är en populär pattern i vissa OR-mappers (och en förutsättning för effektiv ActiveRecord) och det är helt klart funkis i enkla domäner. Jag har använt denna pattern (med Castle ActiveRecord) nyligen i ett kundprojekt, med mycket gott resultat.

Nackdelen är förstås att man riskerar att knyta in datalagret i affärslagret, vilket faktiskt kan vara problematiskt på riktigt (inte bara religiöst betingat), men man bör ge det en chans. I synnerhet om man bara skapar traditonella ASP.NET-lösningar och inte stora distribuerade system.

Medlem sedan sep. 2005673 inlägg
#30

Däremot är det en klart fungerande modell om man gör en mer generell klass för Save-metoderna etc och ärver från den.

Lite nybörjarfunderingar:

Hur gör man en sådan generell klass för exempelvis spara-metoden? En båt och en bil kanske sparas med olika värden i olika tabeller, då får man väl en rörigare save-metod istället än om man har en save i respektive klass som är anpassad för ett visst objekt?

Och varför måste jag ha två klasser, car och cars... varför kan man inte ha allt i en klass? Är det bara för att det är snyggare?

Vad är T och K i PersistableClass<T,K>???

Medlem sedan maj 20012 812 inlägg
#31

P skrev:

Vad var det som fick dig att överge konceptet Gladh, ngt mer än att det blev en herrans massa klasser?

Det tog helt enkelt för lång tid att dela uppe det "enligt konstens alla regler". Tid som en arbetsigivare inte uppskattar. Så det blev till att korta ner utvecklingstiden och "för fula" koden lite, det viktiga för en arbetsgivare är inte hur snygg koden är utan hur bra resultatet blir.

Dock inte sagt att det inte är korrekt att dela upp det, ju större projekt, destu viktigare är det att man lyckas separerar de olika områdena ifrån varandra så att man enkelt kan byta ut något eller hitta var felet är. Ju mindre projekt man har destu mer kan man bryta mot "reglerna"...

- M

Medlem sedan maj 20012 812 inlägg
#32

inspiro skrev:

En båt och en bil kanske sparas med olika värden i olika tabeller, då får man väl en rörigare save-metod istället än om man har en save i respektive klass som är anpassad för ett visst objekt?

Ja och inte bara båt och bil, utan även flyplan och motorcykel och lastbil och....

Som du förstår så måste du flytta ut relationen mellan din klass och din databas någon annanstans och bara låta din save bygga upp en unik save SQL för respektive objekt. Det gör man med metadata. Alltså data som beskriver något, här är ett exempel:

[DataTable("Bilar", Database.SqlServer)]
public class Car
{
      [Identifier(IdentifierType.Sequenze)]
      [DataField("Id")]
      private long _Identifier;

      [DataField("Namn")]
      private string _Name;

      [DataField("Farg")]
      private Color _Color;
}

Här har du en klass som heter Bil, denna klassen innehåller även metadata över hur denns databasstruktur ser ut. Den finns i en tabel som heter Bilar, och databasen är en SQLServer databas. den har fält som heter ID, Namn och Farg, och vi vet även vilka medlemar som skall hämta/spara sina värden till vilka kolumner i databasen.

Man kan nu göra en generell metod som tar emot en Car hämtar ut metadatan från den och bygger ihop en SQL sats och fyller denna med värden från membervariablerna och sedan exekveras till databasen, och vips så har du sparat ner dina bilar till databasen. Och när det gäller båten så ser det ut så här.

[DataTable("Batar", Database.MySq)]
public class Boar
{
      [Identifier(IdentifierType.Sequenze)]
      [DataField("Id")]
      private long _Identifier;

      [DataField("TypAvBat")]
      private string _Type;

      [DataField("Langd")]
      private int _Length;
}

Båda dessa klasser kan nu sparas ner med samma Save() metod, och alla andra nya klasser som du skriver också kan sparas ner med samma savemetod så länge de innehåller samma metadata.

I just detta exempel så ligger metadatan inne i klassen som attributte, det är båda bra och dåligt, så många väljer att lägga dem i separata XML-filer så man skall kunna gå in och ändra i dem utan att man behöver bygga om koden. Man kan även lägga metadatan i en egen databas om man hellre vill det. Det viktiga är inte var det ligger utan att den finns där.

P skrev:

Vad är T och K i PersistableClass<T,K>???

Det har att göra med Generics och i detta exemplet så står T för typen av objekt som du önskar operera på, och K står för din typ av sökdata som du skickar in.

Det betydar att med .Find(T type, K id), så kan du skicka in vilket objekt som helst som T och vilken söksträng som K.

Alltså är det korrekt att skriva.

Bilar.Save(bil, 10);

eller

Batar.Save(bat1, "Kalle")

Båda save metoderna kommer att kalla på samma save metod i Persistans klassen, fast det är olika inputtyper som du anropar den med.

- M

Medlem sedan sep. 2005673 inlägg
#33

Inte helt enkelt att förstå sånt här tycker jag. Alltså, om jag förstått det rätt... DataTable i [DataTable("Bilar", Database.SqlServer)] är en funktion med två parametrar (tabell och typ av db)? Och klassen car bygger upp en sql-sats som på något sätt kommer med in i funktionen DataTable?

Om jag använder stored procedures då? Jag tycker fortfarande att det känns mycket enklare att direkt i carklassen anropa en procedur som stoppar bilen i min db. Har man en bra db-klass så är det ju en rad kod! Och min sql ligger överhuvudtaget inte inbakad i koden (vilket jag har förstått är dåligt?).

Medlem sedan sep. 2005673 inlägg
#34

En fråga till... :) Jo, den här metadatan som du har i din klass:

[DataField("Id")]
private long _Identifier;

Exakt vad är det för något, är det någon slags variabel? En sträng? Hur sätter man den? Varför skriver man DataField("Id") inom hakparenteser?

Medlem sedan dec. 19996 721 inlägg
#35

Det är ett attribut (Attribute). Det finns massor av inbyggda (t.ex. [Serializable]), och så kan man skriva egna också, genom att ärva från basklassen System.Attribute.

Medlem sedan maj 20012 812 inlägg
#36

inspiro skrev:

Om jag använder stored procedures då?

Om du vill använda en SP istället så kan man göra så här:

[DataTable("CarInsert", Databas.SqlServer, DataAction.SP)]
public class car{
}

Eller hur man nu väljer att berätta för koden att man skall anropa en SP istället för att själv bygga upp SQL satsen.

inspiro skrev:

Jag tycker fortfarande att det känns mycket enklare att direkt i carklassen anropa en procedur som stoppar bilen i min db. Har man en bra db-klass så är det ju en rad kod! Och min sql ligger överhuvudtaget inte inbakad i koden (vilket jag har förstått är dåligt?).

Man skall göra det som man tycker känns bäst själv. I ditt fall så har du overhead på en rad kod. Om du tycker det är en smidig lösning så kör på det. Om man väljer en lösning som jag föreslagit så har man en DB-klass som man aldrig behöver ändra i (sanning med modifikation).

Däremot så tyckte jag det lätt intressant hur du lyckas få till så att du endast behöver ändra en rad i din kod när du opererar på en ny klass. Jag har svårt att se den lösningen framför mig, då man måste hämta ut värdet från objektet för att fylla sin SP/SQL sats med. Hade varit intressant att se hur du gör.

inspiro skrev:

Exakt vad är det för något, är det någon slags variabel? En sträng? Hur sätter man den? Varför skriver man DataField("Id") inom hakparenteser?

Precis som emission säger så är det ett attribute och just detta attribute är något som man skriver själv, det finns alltså inte i .NET Frameworket. Fördelen med attribute så här är att jag kan plocka fram just denna membervaribel och få fram just detta attribute för just denna membervariable. Alltså så kan jag i min kod läsa ut att värdet som finns i _Identifier skall sparas till en kolumn i databasen som heter ID. Eller när jag vill hämta värden från databasen så skall värdet i kolumnen ID sparas ner i variablen _Identifier.

Och med hjälp av DataTable attributet så kan man plocka fram att för denna typ av objekt så skall man hänvisa till tabellen Bilar i en SQLServer databas. Man måste givetviss också skicka med kopplingssträngen, men den finns inte definerad i klassen utan någon annanstans

- M

Medlem sedan dec. 19996 721 inlägg
#37

Gladh skrev:

[DataTable("CarInsert", Databas.SqlServer, DataAction.SP)]
public class car{
}

Jag måste erkänna att jag tycker att det är ett rätt konstigt kontext att lägga informationen om databastyp i (Databas.SqlServer).

Medlem sedan juni 20019 024 inlägg
#38

Intressant, den här metoden (metod som i process alltså) har jag letat efter! Det blir ännu mindre kod till ännu mindre arbetsbörda. :)

Medlem sedan maj 20012 812 inlägg
#39

emission skrev:

Jag måste erkänna att jag tycker att det är ett rätt konstigt kontext att lägga informationen om databastyp i (Databas.SqlServer).

Det är ett exempel emission, du kan lägga det på samma ställe som connectionstringen eller lägga alla ut all metadata i en xml-fil, vilket är att rekomendera.

- M

Medlem sedan juni 20003 076 inlägg
#40

Ordet overhead näms ofta i såna här typer av sammanhang och det finns naturligvis ingen regel för var gränsen går men jag måste ändå av nyfikenhet fråga er vad ni anser om följande scenarion (förutsatt att man har en "skapligt" klar bild av hur databasdesign, antal troliga klasser, uppskattat antal besökare osv. ser ut):
Scenario 1:
En site åt ett plåtslageri.
Siten innehåller typ 5-10 sidor (cms) med kanske en nyhetsmodul där kunden skriver lite aktuella nyheter.
Antal besökare är kanske högst 50/dag.
Utvecklingskostnad: 5-10 tusen.

Scenario 2:
En site åt ett lokalt bussbolag.
Siten innehåller typ 20-50 sidor (cms) med moduler för att skapa tidtabeller, nyheter.
Antal besökare är kanske 200-500/dag.
Utvecklingskostnad: 50-100 tusen.

Scenario 3:
En site åt en comunity.
Siten innehåller typ 50< sidor (cms) med moduler: forum, personliga användarsidor, nyheter, div. olika tjänster osv.
Antal besökare är kanske 500</dag.
Utvecklingskostnad: 100 < tusen.

Det jag undrar om detta är helt enkelt:
1. Hur mycket ska separeras i klassväg/scenario?
2. Om man är van att jobba med ORM, är det fortfarande värt att göra scenario 1 med sådan teknik med tanke på vad inkomsten är?
3. Fan, det känns som om jag har tusen frågor och funderingar om detta men får inte ner dem i skrift! :(

Kom gärna med allmänna egna erfarenheter/svar om detta! :)

283 ms totalt · 4 externa anrop · v20260731065814-full.2f471f9e
135 ms — deklarationer (db)
0 ms — hämta statistik (cache)
144 ms — hämta tråd, inlägg och bilagor (db)
132 ms — ändringar (db)