Avatar billede websam Nybegynder
07. oktober 2005 - 11:32 Der er 65 kommentarer og
1 løsning

Hvad er fordelen ved get/set ?

Hejsa,

Jeg sad og læste en artikel om OOP hvor man benytter sig af :

Private _Email As String
Public Property Email() As String
    Get
        Return _Email
    End Get
    Set(ByVal Value As String)
        _Email = Value
    End Set
End Property

Hvor i mod jeg bare har angivet det som en variabel i min function :

Public Function SaveEmail(Byval Email As String)
'--- Laver noget database haløj
End Function

Bliver det mere OOP med det set/get end den måde jeg gør det på ?

/Websam
Avatar billede perlund Nybegynder
07. oktober 2005 - 11:35 #1
Det kommer an på situationen. Men hvis værdien er beskrivende for entiten, plejer man at bruge properties.
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:36 #2
per>

det er en property
Avatar billede softspot Forsker
07. oktober 2005 - 11:37 #3
Umiddelbart er det mere intuitivt at implementere egenskaber på et objekt, hvor man bare tildeler og aflæser værdien som en normal variabel og ikke som funktioner der henter og sætter værdien.
Avatar billede websam Nybegynder
07. oktober 2005 - 11:37 #4
Kan du uddybe det lidt ?
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:39 #5
Det anses for best practice i OOP idag at encapsulate fields med metoder så
man har private fields og public metoder til at tilgå fields med (de metoder
er så properties i .NET men det er en detalje).

Argumentet er at det giver en ekstra fleksibilitet hvsi man vil ændre
i typer eller skal have noget udført når field ændres etc..

Det er som sagt anset for best practice. Det er meget sjældent at det
faktisk udnyttes i praksis.
Avatar billede websam Nybegynder
07. oktober 2005 - 11:43 #6
arne_v >>

Og hvis jeg så forstår dig ret, gør det det lettere for mig selv i tilfælde at et i db f.eks ændres fra char til int (bare som eksempel) så behøver jeg kun at ændre det i min property fra :

Private _Email As String

Til :

Private _Email As Int

Er det i den retning jeg skal forstå det ?
Avatar billede websam Nybegynder
07. oktober 2005 - 11:44 #7
og dog det ska ljo stadigt ændres i min SqlDbType men ellers har jeg nok ikke helt fanget den !?!
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:45 #8
ja

Private _Email As Integer
Public Property Email() As String
    Get
        Return _Email.ToString
    End Get
    Set(ByVal Value As String)
        _Email = Int32.Parse(Value)
    End Set
End Property

vil skjule den rettelse

(rent praktisk er det nok mere interessant at ændre fra Integer til String)
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:46 #9
ideen er som sagt encapsulation

du adskiller dit interface som er de de andre programmører på projektet
kode op imod fra din implementation således at du kan ændre din implementation
uden at der skal rettes i den kode de andre har skrevet som bruger din kode
Avatar billede websam Nybegynder
07. oktober 2005 - 11:49 #10
Ok og når jeg så benytter mig af 3-lags modellen hvor jeg har mit formular/datavisning i form af .aspx og .ascx sider min validering af data fra f.eks en formular i mit buisness lag og ren database tilgang i mit datalag hvor placere jeg så mest korrekt mine property henne ?

Og hvordan tildeler jeg hver enkel property de data der kommer fra formularen ?
Avatar billede websam Nybegynder
07. oktober 2005 - 11:51 #11
Kommentar: arne_v
07/10-2005 11:46:52

Ja den del er jo super cool, på den måde bliver det jo helt OOP når der er flere om opgaven ;o)

/Websam
Avatar billede casper_skovgaard Nybegynder
07. oktober 2005 - 11:55 #12
Det er også muligt at lave en property som kun kan læses. Ved kun at have en get og ingen set.
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:55 #13
det er design på 2 helt forskellige niveauer

1)  du har din 3 lags model

    du sender nogle klasser mellem lagene

    de klasser har nogle data

2)  i de klasser vælger du at have private fields og public properties til at
    tilgå fields
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:56 #14
ja

og fra 2.0 kan man have en public get og en mere restricted set
Avatar billede erikjacobsen Ekspert
07. oktober 2005 - 11:56 #15
Et par små eksempler på anvendelse af get/set. Du har en klasse med "fornavn" og "efternavn".

1) I dine set-metoder for "fornavn" og "efternavn" laver du en exception hvis værdien er tom.
2) Du laver en property kun med get, der hedder "fuldtnavn", som er en sammensætning af "fornavn" og "efternavn".

Altså, kontrol eller behandling af input til klasser, og properties behøver ikke have en 1-1 sammenhæng med felter i klassen.
Avatar billede arne_v Ekspert
07. oktober 2005 - 11:57 #16
jeg mener også at properties kan også laves virtual
Avatar billede websam Nybegynder
07. oktober 2005 - 12:20 #17
Så mistede jeg lige forbindelsen til forståelsen af dette :oO

arne_v >> skal jeg så forstå det således at jeg ikke skal benytte mig af 3 lags modellen hvis jeg vil bruge property's ?

Lad mig komme med lidt kode af hvordan jeg gør det nu :

Design Lag :

Private Sub btn_Newsedit_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Newsedit.Click
    '--- Kalder redigerings funktion
    Dim strNewsAdd As String
    strNewsAdd = objNewsBiz.CheckNews(drp_NewsLanguage.SelectedValue, _
                                          txt_NewsHeadline.Text, _
                                          txt_News.Text, _
                                          Request.QueryString("Id"), _
                                          "Add")
    If Not strNewsAdd = "True" Then
        lbl_NewsError.Text = "<b>" & Util.GetLocalizedString("errorheader") & "</b><br>" & strNewsAdd & "<br>"
    Else
        Response.Redirect("?m=news&a=edit")
    End If
End Sub

dette resultere i at jeg sender diverse til mit buisness lag der tjekker op på om alt er udfyldt osv.

Buisness Lag

(her kommer kun lige et udpluk da det ellers ville blive for meget)

If Modul = "Add" Then
    '--- Tilføjer nyhed til database
    Dim strNewsAdd As String
    strNewsAdd = objNewsData.Newsadd(Language, Headline, News)
    If Not strNewsAdd = "True" Then
        strError = strError & strNewsAdd
        Return strError
    Else
        Return "True"
    End If
End If

og med objNewsData.Newsadd(Language, Headline, News) sender jeg så mine data til indsættelse i min database.

Data Lag :

'--- Function der gemmer nyhed i databasen.
    Public Function Newsadd(ByVal Language As String, ByVal Headline As String, ByVal News As String)
        Dim objDb As DbClass = New DbClass
        Dim objComm = New SqlCommand
        objComm.Parameters.Add("@Language", SqlDbType.VarChar)
        objComm.Parameters.Add("@Headline", SqlDbType.NVarChar)
        objComm.Parameters.Add("@News", SqlDbType.NVarChar)
        objComm.Parameters("@Language").Value = Language
        objComm.Parameters("@Headline").Value = Headline
        objComm.Parameters("@News").Value = News
        objComm.Connection = objDb.SqlConnect
        Try
            objComm.CommandText = "Insert Into tbl_news (getLanguage, Headline, News) Values (@Language, @Headline, @News)"
            objComm.ExecuteNonQuery()
        Catch ex As Exception
            Return ex.Message
        Finally
            objDb.SqlClose()
        End Try
        Return "True"
    End Function

Den er der ikke så meget at skrive om den gemmer blot i databasen.

Hvordan ville jeg skulle lave det hvis jeg benyttede mig af property's istedet ?

/Websam
Avatar billede arne_v Ekspert
07. oktober 2005 - 12:21 #18
nej

men de 2 ting er helt uafhængige
Avatar billede arne_v Ekspert
07. oktober 2005 - 12:24 #19
Det der eksempel ligger ikke vildt meget op til brug af properties, da der
jo ikke er nogen fields ...

Fremfor at sende 3 string argumenter kunne du sende et enkelt objekt med 3 fields
og 3 properties
Avatar billede websam Nybegynder
07. oktober 2005 - 12:26 #20
Og det er så det jeg gerne ville have et eksempel på da jeg ikke forstår hvordan jeg skal bære mig ad med det ;o) ?
Avatar billede arne_v Ekspert
07. oktober 2005 - 12:29 #21
lige ud af landevejen

opret en klasse med de 3 fields/properties

ret metoderne til
Avatar billede websam Nybegynder
07. oktober 2005 - 12:33 #22
Pas, jeg læser noget mere på det når jeg får tid, tak for hjælpen ;o)
Avatar billede websam Nybegynder
07. oktober 2005 - 13:55 #23
Ok her kommer så lige noget test ud fra hvad jeg lige kunne finde ud af. Min klasse med property ser således ud :

Imports System
Imports System.Data
Imports System.Data.SqlClient

Public Class UserData
    Dim objDb As DbClass = New DbClass
    Private _CustomerId As Integer
    Private _Firstname As String
    Private _Lastname As String

    Public ReadOnly Property CustomerId() As Integer
        Get
            Return _CustomerId
        End Get
    End Property

    Public Property Firstname() As String
        Get
            Return _Firstname
        End Get
        Set(ByVal Value As String)
            _Firstname = Value
        End Set
    End Property

    Public Property Lastname() As String
        Get
            Return _Lastname
        End Get
        Set(ByVal Value As String)
            _Lastname = Value
        End Set
    End Property

    Private Sub PopulateUser()
        _CustomerId = 0
        _Firstname = ""
        _Lastname = ""
    End Sub

    Public Sub New(ByVal CustomerId As Integer)
        Dim objConn As New SqlConnection(objDb.SqlConnect)
        Dim objComm As New SqlCommand("Select * From tbl_products Where Id = @CustomerId", objConn)
        objComm.Parameters.Add("@CustomerId", SqlDbType.Int)
        objComm.Parameters("@CustomerId").Value = CustomerId

        Dim Rs As SqlDataReader = objComm.ExecuteReader
        If Rs.Read() Then
            _CustomerId = Rs.GetInt32(0)
            _Firstname = Rs.GetString(2)
            _Lastname = Rs.GetString(3)
        Else
            PopulateUser()
        End If
        Rs.Close()
        objDb.SqlClose()
    End Sub

    Public Function CreateUser() As Integer
        '--- Laver forbindelse til database.
        Dim objConn As New SqlConnection(objDb.SqlConnect)
        '--- Laver sql indsættelse i database.
        Dim objComm As New SqlCommand("Insert Into tbl_login_user "(Firstname, Lastname) Values (@Firstname, @Lastname)", objConn)
        '--- Sætter database parametre.
        objComm.Parameters.Add("@Firstname", SqlDbType.VarChar)
        objComm.Parameters.Add("@Lastname", SqlDbType.NVarChar)
        '--- Sætter value på parametre.
        objComm.Parameters("@Firstname").Value = _Firstname
        objComm.Parameters("@Lastname").Value = _Lastname
        objComm.ExecuteNonQuery()
        objComm.Parameters.Clear()
        '--- Henter ideet fra den netop oprettede record.
        objComm.CommandText = "Select @@Identity"
        Dim newCustomerId As Integer = Convert.ToInt32(objComm.ExecuteScalar)
        objDb.SqlClose()
        _CustomerId = newCustomerId
        '--- Retunere det nye id
        Return newCustomerId
    End Function
End Class

Så har jeg lavet en .aspx side med følgende i pageload :

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Dim objUserData As UserData = New UserData
    objUserData.Firstname = "John"
    objUserData.Lastname = "Doe"
    Dim intUserId As Integer = objUserData.CreateUser
    Response.Write(intUserId)
End Sub

Men der melder den fejl i denne linie :

Dim objUserData As UserData = New UserData

Og Fejlen er :

Argument not specified for parameter 'CustomerId' of 'Public Sub New(CustomerId As Integer)'.

kan du forklare mig hvad jeg gør galt ?

/Websam
Avatar billede arne_v Ekspert
07. oktober 2005 - 14:00 #24
New UserData

skal være

New UserData(id)
Avatar billede websam Nybegynder
07. oktober 2005 - 14:02 #25
Ja, men da jeg forsøger at oprette en ny record har jeg jo ikke noget id ?
Avatar billede arne_v Ekspert
07. oktober 2005 - 14:03 #26
så lav en constructor uden argument
Avatar billede websam Nybegynder
07. oktober 2005 - 14:28 #27
Ideen var jo at jeg kunne bruge min constructor til både insert, update og delete og ved at tilføre et 0 får jeg indsat mine data som jeg gerne vil.

Men er det ellers måden er gribe det an på det jeg her har lavet ?

/Websam
Avatar billede arne_v Ekspert
07. oktober 2005 - 14:31 #28
du kan godt have 2 constructorer i en klasse med forskellige argumenter

i C# kan du kalde en constructor fra en anden constructor - det ved jeg ikke
om du kan i VB.NET
Avatar billede arne_v Ekspert
07. oktober 2005 - 14:32 #29
jeg ville nok have adskilt tingene i
  - en klasse som kun indeholder data
  - en klasse som kan læse/skrive den første klasse fra/til databasen
Avatar billede websam Nybegynder
07. oktober 2005 - 14:37 #30
Kommentar: arne_v
07/10-2005 14:31:00

Dermed forstået at jeg kan lave :

Public Sub New()
'--- noget kode
End Sub

Og en :

Public Sub New(Byval Id As Integer)
'--- noget kode
End Sub

Kommentar: arne_v
07/10-2005 14:32:01

Det er så her jeg ikke helt forstår hvorledes jeg har mine property's i en klasse og mit databehanling i en anden klasse ?

/Websam
Avatar billede arne_v Ekspert
07. oktober 2005 - 14:41 #31
ja

----

fordi din data klasse er noget du sender mellem dine lag

mens selve database tilganeg kun hører hjemme i database laget

måske beslutter du dig for at droppe databasen og bruge XML filer i stedetfor

det angår jo ikke dit præsentations lag eller dit forretnings logik lag
Avatar billede websam Nybegynder
07. oktober 2005 - 14:47 #32
Ja, men hvis jeg har forstået det hele rigtigt så skal jeg vel stadigvæk kalde mit datalag i mit buisness lag for overhovedet at have adgang til mit datalag derfra ?

Og det samme gør sig vel gældende imellem mit design lag og mit buisness lag ?
Avatar billede arne_v Ekspert
07. oktober 2005 - 14:50 #33
ja

så enten skal dit foretnings logik kende din dtabase tilgangs klasse

hvilket er nemt men ikek så kønt (men stadig langt kønnere end at slæbe
en data klasse med database logik i hele vejen rundt i systemet)

eller så encapsulater du endnu mere og kalder en factory klasse
som returnerer et interface til at stå for database adgangen
Avatar billede websam Nybegynder
07. oktober 2005 - 14:57 #34
Men hvis jeg så skal lave ovenstående om således at jeg har de 3 lag kan du så give mig et eksempel på hvor ledes jeg gør det ?

############################################################################

"eller så encapsulater du endnu mere og kalder en factory klasse
som returnerer et interface til at stå for database adgangen"

Kan du uddybe det ?

/Websam
Avatar billede arne_v Ekspert
07. oktober 2005 - 15:00 #35
ja

men det vil tage lidt tid at skrive

så det bliver nok først i aften eller imorgen
Avatar billede websam Nybegynder
07. oktober 2005 - 15:41 #36
Ok jeg venter gerne :o)
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:00 #37
jeg har noget C# kode nu - vil du have det - eller skal jeg konvertere til VB.NET ?
Avatar billede websam Nybegynder
09. oktober 2005 - 00:02 #38
du kan sende det til mig jeg har en converter der nok skulle kunne klare det, du kan evt. sende det til martin(a)newage.dk
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:04 #39
jeg ville da poste det her
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:05 #40
<%@ Page Inherits="PL.Transfer" %>
<html>
<head>
<title>Money transfer</title>
</head>
<body>
<form id="f" method="post" runat="server">
From account: <asp:TextBox id="from" runat="server"/>
<br>
To account: <asp:TextBox id="to" runat="server"/>
<br>
Amount: <asp:TextBox id="amount" runat="server"/>
<br>
<asp:Button id="transfer" text="Transfer" runat="server"/>
<br>
<asp:Label id="status" runat="server"/>
</form>
</body>
</html>
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:06 #41
using System;
using System.Web.UI;
using System.Web.UI.WebControls;

using BLL;

namespace PL
{
    public class Transfer : Page
    {
        protected TextBox from;
        protected TextBox to;
        protected TextBox amount;
        protected Button transfer;
        protected Label status;
        private TransferManager tm;
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            transfer.Click += new EventHandler(transfer_Click);
            tm = TransferManagerFactory.Create("strict");
        }
        protected void transfer_Click(object sender, EventArgs e)
        {
            if(tm.ExecuteTransfer(new TransferData(from.Text, to.Text, decimal.Parse(amount.Text))))
            {
                from.Text = "";
                to.Text = "";
                amount.Text = "";
                status.Text = "Money transferred";
            }
            else
            {
                status.Text = "Money not transferred";
            }
        }
    }
}
Avatar billede websam Nybegynder
09. oktober 2005 - 00:06 #42
det er ok post løs ;o) det er bare mig der tolker lidt forkert her sidst på dagen
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:06 #43
using System;

using DAL;

namespace BLL
{
    public class TransferData
    {
        private string from;
        private string to;
        private decimal amount;
        public TransferData() : this("", "", 0.00m)
        {
        }
        public TransferData(string from, string to, decimal amount)
        {
            this.from = from;
            this.to = to;
            this.amount = amount;
        }
        public string From
        {
            get
            {
                return from;
            }
            set
            {
                from = value;
            }
        }
        public string To
        {
            get
            {
                return to;
            }
            set
            {
                to = value;
            }
        }
        public decimal Amount
        {
            get
            {
                return amount;
            }
            set
            {
                amount = value;
            }
        }
    }
    public interface TransferManager
    {
        bool ExecuteTransfer(TransferData xfer);
    }
    public class TransferManagerFactory
    {
        public static TransferManager Create(string tmtyp)
        {
            if(tmtyp == "standard")
            {
                return new StandardTransferManager();
            }
            if(tmtyp == "strict")
            {
                return new StrictTransferManager();
            }
            else
            {
                return null;
            }
        }
    }
    internal class StandardTransferManager : TransferManager
    {
        private AccountManager am;
        public StandardTransferManager()
        {
            am = AccountManagerFactory.Create("XML");
        }
        public bool ExecuteTransfer(TransferData xfer)
        {
            AccountData from = am.Load(xfer.From);
            AccountData to = am.Load(xfer.To);
            if(from != null && to != null && from.Amount >= xfer.Amount)
            {
                from.Amount -= xfer.Amount;
                to.Amount += xfer.Amount;
                am.Save(from);
                am.Save(to);
                return true;
            }
            else
            {
                return false;
            }
        }
    }
    internal class StrictTransferManager : TransferManager
    {
        private const int TRANSFER_LIMIT = 1000;
        private const int ACCOUNT_LIMIT = 10000;
        private AccountManager am;
        public StrictTransferManager()
        {
            am = AccountManagerFactory.Create("DB");
        }
        public bool ExecuteTransfer(TransferData xfer)
        {
            AccountData from = am.Load(xfer.From);
            AccountData to = am.Load(xfer.To);
            if(from != null && to != null && from.Amount >= xfer.Amount &&
              xfer.Amount <= TRANSFER_LIMIT && (to.Amount + xfer.Amount) <= ACCOUNT_LIMIT)
            {
                from.Amount -= xfer.Amount;
                to.Amount += xfer.Amount;
                am.Save(from);
                am.Save(to);
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:06 #44
using System;
using System.IO;
using System.Xml;
using System.Data.OleDb;

namespace DAL
{
    public class AccountData
    {
        private string name;
        private decimal amount;
        public AccountData() : this("", 0.00m)
        {
        }
        public AccountData(string name, decimal amount)
        {
            this.name = name;
            this.amount = amount;
        }
        public string Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }
        public decimal Amount
        {
            get
            {
                return amount;
            }
            set
            {
                amount = value;
            }
        }
    }
    public interface AccountManager
    {
        AccountData Load(string name);
        void Save(AccountData ac);
    }
    public class AccountManagerFactory
    {
        public static AccountManager Create(string amtyp)
        {
            if(amtyp == "DB")
            {
                return new DatabaseAccountManager();
            }
            if(amtyp == "XML")
            {
                return new XmlAccountManager();
            }
            else
            {
                return null;
            }
        }
    }
    internal class DatabaseAccountManager : AccountManager
    {
        public AccountData Load(string name)
        {
            AccountData res;
            OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Databases\\MSAccess\\Test.mdb");
            con.Open();
            OleDbCommand sel = new OleDbCommand("SELECT amount FROM accounts WHERE name='" + name + "'", con);
            OleDbDataReader rdr = sel.ExecuteReader();
            if(rdr.Read())
            {
                decimal amount = (decimal)rdr[0];
                res = new AccountData(name, amount);
            }
            else
            {
                res = null;
            }
            con.Close();
            return res;
        }
        public void Save(AccountData ac)
        {
            OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\Databases\\MSAccess\\Test.mdb");
            con.Open();
            OleDbCommand upd = new OleDbCommand("UPDATE accounts SET amount = " + ac.Amount + " WHERE name='" + ac.Name + "'", con);
            upd.ExecuteNonQuery();
        }
    }
    internal class XmlAccountManager : AccountManager
    {
        private const string XML_FILE = @"C:\accounts.xml";
        private XmlDocument doc;
        public XmlAccountManager()
        {
            doc = new XmlDocument();
            doc.Load(XML_FILE);
        }
        public AccountData Load(string name)
        {
            XmlNode n = doc.SelectSingleNode("//accounts/account[@name='" + name +"']");
            if(n != null)
            {
                decimal amount = decimal.Parse(n.Attributes["amount"].Value);
                return new AccountData(name, amount);
            }
            else
            {
                return null;
            }
        }
        public void Save(AccountData ac)
        {
            XmlNode n = doc.SelectSingleNode("//accounts/account[@name='" + ac.Name +"']");
            n.Attributes["amount"].Value = ac.Amount.ToString();
            StreamWriter sw = new StreamWriter(XML_FILE);
            doc.Save(sw);
            sw.Close();
        }
    }
}
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:08 #45
Det er et dårligt presentation layer, et dårligt business logic layer og et
dårligt data access layer - men det skulle gerne illustrere seperationen
mellem layers på en fornuftig måde
Avatar billede arne_v Ekspert
09. oktober 2005 - 00:09 #46
Jeg arbejder iøvrigt ikke med .NET professionelt, så ovenstående er et udtryk
for hvad der (efter min mening) er en god løsning - ikke nødvendigvis for
hvad der er en almindelig brugt løsning
Avatar billede websam Nybegynder
09. oktober 2005 - 00:23 #47
Jeg surfet som en vild siden i går for at finde mere data og hver gang rammer jeg ind i løsninger der mere eller mindre ligner noget i stil af det jeg allere laver :

Design lag :

Indeholder diverse buttons, labels, textboxes osv. med events på buttons der så trækker i buisness laget

Buisness lag :

Indeholder functioner/metoder der validere data fra design laget og hvis disse er ok kalder man datalaget for at hente, gemme el. manupulere diverse data.

Data lag :

Data laget bliver udelukkende brugt til håndtering af adgangen til databasen.

Det jeg så var ude efter var en måde hvorpå jeg kunne få property inplemteret i min nuværende programmerings facon og i samme ombæring udvide horisonten med noget mere viden på .net siden med brugen af property's. Det bliver bare ikke brugt ret mange steder  efter hvad jeg har kunne finde.

Når det så er sagt vil se om ikke jeg kan se mig ud af den kode du lige har postet når jeg har fået det oversat til vb

/Websam
Avatar billede erikjacobsen Ekspert
09. oktober 2005 - 00:27 #48
Du skal ikke se properties som løsningen på din arkitektur med 3 lag. Tænk på det som:

1) Du designer en arkitektur med 3 (eller flere/færre) lag
2) Du designer en række klasser med passende funktionalitet

Hertil er der ingen, der siger et kuk om properties

3) I hver enkelt klasse, der udspringer af 1) og 2) beskytter du private felter med properties.
Avatar billede websam Nybegynder
09. oktober 2005 - 00:37 #49
Nej, nej jeg ser det heller ikke som løsningen, mere som en måde hvorpå jeg lettere kan rette i min kode hvis dette er tilfældet.

Og jeg har i reglen også flere lag end 3 lag da jeg har klasser til diverse andre ting som f.eks validering, sikkerhed(kryptering og des lige) osv. disse er jo blot lavet for netop at gøre det hele lettere for selv, en form for OOP, da jeg så kan genbruge koden om og om igen i alle de moduler jeg får lavet med tiden.

Men det har været en meget interessant tråd og den har i hvert fald givet grobund for vider udvikling af min viden til .net

/Websam
Avatar billede arne_v Ekspert
09. oktober 2005 - 12:32 #50
det er nok hurtigere hvis jeg konverterer

der var faktisk et par små anti-VB'ismer:
  - samme navn til field og property
  - brug af to som variabel navn
  etc.
Avatar billede arne_v Ekspert
09. oktober 2005 - 12:32 #51
<%@ Page Inherits="PL.Transfer" %>
<html>
<head>
<title>Money transfer</title>
</head>
<body>
<form id="f" method="post" runat="server">
From account: <asp:TextBox id="fromaccount" runat="server"/>
<br>
To account: <asp:TextBox id="toaccount" runat="server"/>
<br>
Amount: <asp:TextBox id="amount" runat="server"/>
<br>
<asp:Button id="transfer" text="Transfer" runat="server"/>
<br>
<asp:Label id="status" runat="server"/>
</form>
</body>
</html>
Avatar billede arne_v Ekspert
09. oktober 2005 - 12:32 #52
Imports System
Imports System.Web.UI
Imports System.Web.UI.WebControls

Imports BLL

Namespace PL

    Public Class Transfer
    Inherits Page
        Protected fromaccount As TextBox
        Protected toaccount As TextBox
        Protected amount As TextBox
        Protected transfer As Button
        Protected status As Label
        Private tm As TransferManager

        Protected Overloads Overrides Sub OnInit(ByVal e As EventArgs)
            MyBase.OnInit(e)
            AddHandler transfer.Click, AddressOf transfer_Click
            tm = TransferManagerFactory.Create("strict")
        End Sub

        Protected Sub transfer_Click(ByVal sender As Object, ByVal e As EventArgs)
            If tm.ExecuteTransfer(New TransferData(fromaccount.Text, toaccount.Text, Decimal.Parse(amount.Text))) Then
                fromaccount.Text = ""
                toaccount.Text = ""
                amount.Text = ""
                status.Text = "Money transferred"
            Else
                status.Text = "Money not transferred"
            End If
        End Sub
    End Class
End Namespace
Avatar billede arne_v Ekspert
09. oktober 2005 - 12:32 #53
Imports System

Imports DAL

Namespace BLL

    Public Class TransferData
        Private _fromaccount As String
        Private _toaccount As String
        Private _amount As Decimal

        Public Sub New()
            MyClass.New("", "", 0D)
        End Sub

        Public Sub New(ByVal fromaccount As String, ByVal toaccount As String, ByVal amount As Decimal)
            _fromaccount = fromaccount
            _toaccount = toaccount
            _amount = amount
        End Sub

        Public Property Fromaccount() As String
            Get
                Return _fromaccount
            End Get
            Set
                _fromaccount = value
            End Set
        End Property

        Public Property Toaccount() As String
            Get
                Return _toaccount
            End Get
            Set
                _toaccount = value
            End Set
        End Property

        Public Property Amount() As Decimal
            Get
                Return _amount
            End Get
            Set
                _amount = value
            End Set
        End Property
    End Class

    Public Interface TransferManager

        Function ExecuteTransfer(ByVal xfer As TransferData) As Boolean
    End Interface

    Public Class TransferManagerFactory

        Public Shared Function Create(ByVal tmtyp As String) As TransferManager
            If tmtyp = "standard" Then
                Return New StandardTransferManager
            End If
            If tmtyp = "strict" Then
                Return New StrictTransferManager
            Else
                Return Nothing
            End If
        End Function
    End Class

    Friend Class StandardTransferManager
    Implements TransferManager
        Private am As AccountManager

        Public Sub New()
            am = AccountManagerFactory.Create("XML")
        End Sub

        Public Function ExecuteTransfer(ByVal xfer As TransferData) As Boolean Implements TransferManager.ExecuteTransfer
            Dim fromaccount As AccountData = am.Load(xfer.Fromaccount)
            Dim toaccount As AccountData = am.Load(xfer.Toaccount)
            If Not (fromaccount Is Nothing) AndAlso Not (toaccount Is Nothing) AndAlso fromaccount.Amount >= xfer.Amount Then
                fromaccount.Amount -= xfer.Amount
                toaccount.Amount += xfer.Amount
                am.Save(fromaccount)
                am.Save(toaccount)
                Return True
            Else
                Return False
            End If
        End Function
    End Class

    Friend Class StrictTransferManager
    Implements TransferManager
        Private Const TRANSFER_LIMIT As Integer = 1000
        Private Const ACCOUNT_LIMIT As Integer = 10000
        Private am As AccountManager

        Public Sub New()
            am = AccountManagerFactory.Create("DB")
        End Sub

        Public Function ExecuteTransfer(ByVal xfer As TransferData) As Boolean Implements TransferManager.ExecuteTransfer
            Dim fromaccount As AccountData = am.Load(xfer.Fromaccount)
            Dim toaccount As AccountData = am.Load(xfer.Toaccount)
            If Not (fromaccount Is Nothing) AndAlso Not (toaccount Is Nothing) AndAlso fromaccount.Amount >= xfer.Amount AndAlso xfer.Amount <= TRANSFER_LIMIT AndAlso (toaccount.Amount + xfer.Amount) <= ACCOUNT_LIMIT Then
                fromaccount.Amount -= xfer.Amount
                toaccount.Amount += xfer.Amount
                am.Save(fromaccount)
                am.Save(toaccount)
                Return True
            Else
                Return False
            End If
        End Function
    End Class
End Namespace
Avatar billede arne_v Ekspert
09. oktober 2005 - 12:32 #54
Imports System
Imports System.IO
Imports System.Xml
Imports System.Data.OleDb

Namespace DAL

    Public Class AccountData
        Private _name As String
        Private _amount As Decimal

        Public Sub New()
            MyClass.New("", 0D)
        End Sub

        Public Sub New(ByVal name As String, ByVal amount As Decimal)
            _name = name
            _amount = amount
        End Sub

        Public Property Name() As String
            Get
                Return _name
            End Get
            Set
                _name = value
            End Set
        End Property

        Public Property Amount() As Decimal
            Get
                Return _amount
            End Get
            Set
                _amount = value
            End Set
        End Property
    End Class

    Public Interface AccountManager

        Function Load(ByVal name As String) As AccountData

        Sub Save(ByVal ac As AccountData)
    End Interface

    Public Class AccountManagerFactory

        Public Shared Function Create(ByVal amtyp As String) As AccountManager
            If amtyp = "DB" Then
                Return New DatabaseAccountManager
            End If
            If amtyp = "XML" Then
                Return New XmlAccountManager
            Else
                Return Nothing
            End If
        End Function
    End Class

    Friend Class DatabaseAccountManager
    Implements AccountManager

        Public Function Load(ByVal name As String) As AccountData Implements AccountManager.Load
            Dim res As AccountData
            Dim con As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Databases\MSAccess\Test.mdb")
            con.Open
            Dim sel As OleDbCommand = New OleDbCommand("SELECT amount FROM accounts WHERE name='" & name & "'", con)
            Dim rdr As OleDbDataReader = sel.ExecuteReader
            If rdr.Read Then
                Dim amount As Decimal = CType(rdr(0), Decimal)
                res = New AccountData(name, amount)
            Else
                res = Nothing
            End If
            con.Close
            Return res
        End Function

        Public Sub Save(ByVal ac As AccountData) Implements AccountManager.Save
            Dim con As OleDbConnection = New OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Databases\MSAccess\Test.mdb")
            con.Open
            Dim upd As OleDbCommand = New OleDbCommand("UPDATE accounts SET amount = " & ac.Amount & " WHERE name='" & ac.Name & "'", con)
            upd.ExecuteNonQuery
        End Sub
    End Class

    Friend Class XmlAccountManager
    Implements AccountManager
        Private Const XML_FILE As String = "C:\accounts.xml"
        Private doc As XmlDocument

        Public Sub New()
            doc = New XmlDocument
            doc.Load(XML_FILE)
        End Sub

        Public Function Load(ByVal name As String) As AccountData Implements AccountManager.Load
            Dim n As XmlNode = doc.SelectSingleNode("//accounts/account[@name='" & name & "']")
            If Not (n Is Nothing) Then
                Dim amount As Decimal = Decimal.Parse(n.Attributes("amount").Value)
                Return New AccountData(name, amount)
            Else
                Return Nothing
            End If
        End Function

        Public Sub Save(ByVal ac As AccountData) Implements AccountManager.Save
            Dim n As XmlNode = doc.SelectSingleNode("//accounts/account[@name='" & ac.Name & "']")
            n.Attributes("amount").Value = ac.Amount.ToString
            Dim sw As StreamWriter = New StreamWriter(XML_FILE)
            doc.Save(sw)
            sw.Close
        End Sub
    End Class
End Namespace
Avatar billede arne_v Ekspert
09. oktober 2005 - 12:32 #55
og et svar
Avatar billede websam Nybegynder
09. oktober 2005 - 18:16 #56
Det var da pænt gjort, men den converter jeg benytter, kom ikke med nogle fejl da jeg lavede det om.

/Websam
Avatar billede websam Nybegynder
02. marts 2006 - 21:47 #57
Når man lige kigger på denne :

Public Class TransferManagerFactory

        Public Shared Function Create(ByVal tmtyp As String) As TransferManager
            If tmtyp = "standard" Then
                Return New StandardTransferManager
            End If
            If tmtyp = "strict" Then
                Return New StrictTransferManager
            Else
                Return Nothing
            End If
        End Function
    End Class

Som jeg går udfra skulle en factory klasse så er den vel nærmest overflødig hvis ikke xml er inde i billedet ?

/Websam
Avatar billede arne_v Ekspert
02. marts 2006 - 23:10 #58
nu er den factory jo til business laget

den anden factory til data laget vil altid returnere det samme hvis der kun
er en implementation (og hvis man fjernede XML i ovenstaaende kode saa ville
der kune vaere en)

men husk at der er forskel paa "kun en nu" og "der vil altid kun vaere en"
Avatar billede websam Nybegynder
02. marts 2006 - 23:20 #59
Ja jeg kan sagtens se muligheder i det hvis nu jeg skulle komme i tanke anden database end lige mssql. Med en implementering af både mysql og oracel ville det jo lige pludselig se helt anderledes ud. Der skulle der så nok bare bruges nogle instillinger i min web.config til at vælge database så det kun skulle vælges der fremfor den manuelle måde du gør det på med "strict/standart", "xml/db".

Men det har helt sikkert givet grudlag for tanke virksomhed.

Jeg beklager mine mange spørgsmål, men hvis ikke jeg spørger bliver jeg jo aldrig klogere ;o)
Avatar billede arne_v Ekspert
02. marts 2006 - 23:39 #60
forskellige database boer kunne laves om i en konfiguratiosn fil, hvis man bruger
interfaces overalt

men man kunne forestille sig varianter:
  raa SQL og data reader
  stored procedures og data reader
  O/R mapping tool

de ville ikke kunne klares uden store aendringer i koden
Avatar billede arne_v Ekspert
02. marts 2006 - 23:41 #61
jeg har ikke beklaget mig over dine spoergsmaal - fordi du har 100% ret

:-)
Avatar billede websam Nybegynder
02. marts 2006 - 23:53 #62
Som det ser ud lige pt. så har jeg kun interface i mit datalag og ikke i business laget da jeg ikke helt har kunne se nogen grund til det.

Men når der, som i dit eksempel ovenfor, er factory klasser med bliver det jo en anden snak da disse i samarbejde med interfacet hjælper med til at skille tingene endnu mere ad.

Så jeg vil klart tage mig tid til at kigge noget mere på det ;o) Men da jeg sidder på egen hånd, på freelance basis og har rigeligt med arbejde så er det sq knapt med tiden.

/Websam
Avatar billede arne_v Ekspert
02. marts 2006 - 23:59 #63
det er nok ogsaa lidt usaedvaneligt med pluggable business lag

men jeg ville jo lave et paedagogisk eksempel
Avatar billede websam Nybegynder
03. marts 2006 - 00:18 #64
Ville øvelsen for mit vekommende så ikke være noget i stil med at jeg laver følgende :

1. Info.vb - klassen med default constructors der loader hindanden, fields og properties som kan bruges på tværs af PL, BLL og DAL

2. Business.vb - klassen (evt. med et interface) hvor jeg fortager mig alt hvad der skal til for at kunne håndtere mine data med mindst mulig skrivning af kode i mit PL

3. Factory.vb - Der kunne være bestemmende for hvilken database der skal benyttes.
    MsSql.vb - Interface og klasse der håndtere data til og fra mssql server.
    Access.vb - Interface og klasse der håndtere data til og fra access database.

Det kunne for virke som en ok løsning med mindre at jeg blot skal lave database valget i min web.config og så droppe Factory.vb.

/websam
Avatar billede arne_v Ekspert
03. marts 2006 - 03:32 #65
23:39:46

factory er ikke noedvendigt til kun database type
Avatar billede websam Nybegynder
05. marts 2006 - 20:42 #66
Det var (tror jeg) fordi at jeg ikke helt forstået ideen med en factory klasse, men ret mig hvis ikke jeg har styr på det.

1. Factory klassen i DAL skal bruges til at loade freind klasserne.
2. Factory klassen i BLL loader dennes freind klasser.
    Med "Private _DAL As Min_Datalags_Klasse" trækker jeg så i datalaget igennem dennes factory klasse.
3. Det samme gør sig så også gældende for BLL i PL

På den måde har jeg så 100 % adskildt de forskellige lag fra hindanden lige bortset fra min info klasse som jeg jo vil bruge på tværs af de forskellige lag for at spare tid med at skrive for meget kode.

Er jeg så ikke ved at være på rette vej ?

/Websam
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester