Den moderne arbejdsplads er i stigende grad afhængig af mødelokaler til at fremme samarbejde, men dette skift medfører også stigende sikkerhedsudfordringer.
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.
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.
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 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)
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
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 ?
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.
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 ?
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)'.
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 ?
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
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
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
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
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
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
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
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 ?
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"
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)
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.
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.
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
Synes godt om
Ny brugerNybegynder
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.