Avatar billede CodingJoe Nybegynder
17. januar 2013 - 09:27 Der er 19 kommentarer og
1 løsning

At undgå genganger af samme parameter på hver metode

Dette er et kodedesign spørgsmål.

Jeg har fx. Klasse X med Metode A, Metode B, Metode C.
Jeg har også en Klasse Y med Metode D, Metode E.

Fælles for alle metoderne er at et credential objekt skal sendes med som parameter.

Nu er den simple tilgang jo at sende det med på følgende måde:
public void GemFoo(FooBar foobar, Credential cred) på samtlige metoder under Klasse X og Klasse Y. Selvom det er tidligere at gøre det på den måde, har Credential objektet ikke rigtig nogen vigtighed i min metode. Det er blot et krav for noget underliggende at fx logge hvem, der har kaldt mine metoder.

Jeg kunne godt tænke mig nogle foreslag til, hvordan kodedesignet bør / kan være. Jeg forestillet mig fx man sender credential oplysningerne med som en del af min constructor på min klasse X og klasse Y.

Ellers lutter jeg øre på de forskellige foreslag.
Avatar billede lclemens Nybegynder
17. januar 2013 - 11:03 #1
Overloading af metoderne måske...

public void GemFoo(FooBar foobar, Credential cred)
{
}

public void GemFoo(FooBar foobar)
{
    GemFoo(foobar, m_Credential);
}

Så kan du selv vælge, hvem du vil kalde... GemFoo(x) eller GemFoo (x, y)
Avatar billede softspot Forsker
17. januar 2013 - 11:55 #2
Jeg ville nok også vælge at sende den med som constructor-parameter, men det afhænger naturligvis af, hvordan klassen i øvrigt er skruet sammen og hvad der ellers er af funktionalitet. Til logging-formål lyder det dog som en oplagt constructor-parameter i mine øre...
Avatar billede CodingJoe Nybegynder
17. januar 2013 - 13:23 #3
Jeg vil ikke overloade. Jeg forsøger at holde metoderne enkle i deres natur sammen med signaturen. Fordi Credentials skal gå igennem på samtlige af mine klasser, tænkte jeg på at lave en abstract klasse, der nedarves i alle mine underklasser og hvis formål er at holde på et credentials objekt. Dog skal den kunne sættes udefra.

Constructor løsningen er i mine øjne også mest oplagt.
Avatar billede arne_v Ekspert
17. januar 2013 - 15:32 #4
Hvis credentials er knyttet til instansen af X/Y og ikke til de enkelte metode kald, saa skal det naturligvis vaere et felt som saettes enten via constructor eller property.

Men kan metoder i samme instans kaldes med forskellige credentials, saa duer det ikke.

Hvis dette er tilfaeldet og samme instans bruges af flere traade, saa skal credential sendes med over i hvert kald.

Hvis det ikke er tilfaeldet, men hver instans kun bruges i en traad, saa kunne du kigge paa noget AOP.
Avatar billede CodingJoe Nybegynder
17. januar 2013 - 16:20 #5
Det er det sidste tilfælde...AOP står for? :)
Avatar billede arne_v Ekspert
17. januar 2013 - 16:42 #6
Aspect Oriented Programming

Det kan laves baade statisk og dynamisk.

Med statisk saa koerer man et lille tool som konverterer EXE/DLL udfra nogle regler.

Man kan f.eks. erstatte et enkelt kald med et antal kald.

Pointen er at man ved at vedligeholde en regel et sted kan undgaa at klatte en masse kode til med noget trivielt kode.

Det er ikke sikkert at det kan bruges i dit tilfaelde. Langtfra sikkert.

Men det er en mulighed som er vaerd at overveje.

Hvad vaerre er at static AOP frameworks til .NET er en noget grumset affaere.
Avatar billede CodingJoe Nybegynder
17. januar 2013 - 16:48 #7
Ja, slog det op på nettet...sidder og læser om det lige nu :)
Avatar billede janus_007 Nybegynder
19. januar 2013 - 23:37 #8
Du kunne jo lave Credential singleton og så bare oprette en instans i de klasser som skal bruge den. Initielt tænker jeg at den skal oprettes ifb. med noget login af en art.


AOP vil jeg nok mene ikke fungerer til dit scenarie... det er forøvrigt også et "forkert" pattern :)
Avatar billede CodingJoe Nybegynder
20. januar 2013 - 16:37 #9
En singleton vil da ikke fungere i dette scenarie, da credentials beskriver, den bruger der benytter sig af systemet.

Smider jeg brugeren ind i en singleton, har jeg låst min applikation til een enkelt bruger... :(
Avatar billede arne_v Ekspert
20. januar 2013 - 21:37 #10
Dette her bliver en masse kode.

Lad os foerst starte med et eksempel uden log:


using System;

namespace E
{
    public class Foo
    {
        public void A()
        {
            Console.WriteLine("A");
        }
        public void B()
        {
            Console.WriteLine("B");
        }
    }
    public class Bar
    {
        public void C()
        {
            Console.WriteLine("C");
        }
        public void D()
        {
            Console.WriteLine("D");
        }
    }
    public class ApplicationLogic
    {
        public void DoSomethings()
        {
            Foo foo = new Foo();
            foo.A();
            foo.B();
            Bar bar = new Bar();
            bar.C();
            bar.D();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}


Problem:
* der oenskes logget hvem der har kaldt Foo og Bat metoderne
Avatar billede arne_v Ekspert
20. januar 2013 - 21:39 #11
Med parameter i hvert kaldt vil det se ud som:


using System;

namespace E
{
    public class Person
    {
        public string Name { get; set; }
    }
    public class Foo
    {
        public void A(Person p)
        {
            Console.WriteLine(p.Name + " called Foo.A");
            Console.WriteLine("A");
        }
        public void B(Person p)
        {
            Console.WriteLine(p.Name + " called Foo.B");
            Console.WriteLine("B");
        }
    }
    public class Bar
    {
        public void C(Person p)
        {
            Console.WriteLine(p.Name + " called Bar.C");
            Console.WriteLine("C");
        }
        public void D(Person p)
        {
            Console.WriteLine(p.Name + " called Bar.D");
            Console.WriteLine("D");
        }
    }
    public class ApplicationLogic
    {
        public void DoSomethings()
        {
            Person p = new Person { Name="Arne" };
            Foo foo = new Foo();
            foo.A(p);
            foo.B(p);
            Bar bar = new Bar();
            bar.C(p);
            bar.D(p);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}


Problem:
* den kaldende kode er svinet til
* den kaldte kode er svinet til
Avatar billede arne_v Ekspert
20. januar 2013 - 21:43 #12
Det kan pyntes lidt ved at flytte parameteren til konstruktor:


using System;

namespace E
{
    public class Person
    {
        public string Name { get; set; }
    }
    public class Foo
    {
        private Person p;
        public Foo(Person p)
        {
            this.p = p;
        }
        public void A()
        {
            Console.WriteLine(p.Name + " called Foo.A");
            Console.WriteLine("A");
        }
        public void B()
        {
            Console.WriteLine(p.Name + " called Foo.B");
            Console.WriteLine("B");
        }
    }
    public class Bar
    {
        private Person p;
        public Bar(Person p)
        {
            this.p = p;
        }
        public void C()
        {
            Console.WriteLine(p.Name + " called Bar.C");
            Console.WriteLine("C");
        }
        public void D()
        {
            Console.WriteLine(p.Name + " called Bar.D");
            Console.WriteLine("D");
        }
    }
    public class ApplicationLogic
    {
        public void DoSomethings()
        {
            Person p = new Person { Name="Arne" };
            Foo foo = new Foo(p);
            foo.A();
            foo.B();
            Bar bar = new Bar(p);
            bar.C();
            bar.D();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}


Men der er stadig noget svineri paa begge sider.
Avatar billede arne_v Ekspert
20. januar 2013 - 21:44 #13
Ved at skifte til en singleton kan man pynte op den kaldende side:


using System;

namespace E
{
    public class Person
    {
        public string Name { get; set; }
    }
    public class ResponsibleHolder
    {
        private static ResponsibleHolder instance = null;
        private static object mylock = new object();
        private ResponsibleHolder()
        {
        }
        public static ResponsibleHolder Instance
        {
            get
            {
                lock(mylock)
                {
                    if(instance == null)
                    {
                        instance = new ResponsibleHolder();
                    }
                }
                return instance;
            }
        }
        public Person Responsible { get; set; }
    }
    public class Foo
    {
        public void A()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Foo.A");
            Console.WriteLine("A");
        }
        public void B()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Foo.B");
            Console.WriteLine("B");
        }
    }
    public class Bar
    {
        public void C()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Bar.C");
            Console.WriteLine("C");
        }
        public void D()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Bar.D");
            Console.WriteLine("D");
        }
    }
    public class ApplicationLogic
    {
        public void DoSomethings()
        {
            ResponsibleHolder.Instance.Responsible = new Person { Name="Arne" };
            Foo foo = new Foo();
            foo.A();
            foo.B();
            Bar bar = new Bar();
            bar.C();
            bar.D();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}
Avatar billede arne_v Ekspert
20. januar 2013 - 21:45 #14
Problemer:
* det ser stadig ikke godt ud paa den kaldte side
* det virker ikke i en multithreaded context

Det sidste kan dog fixes:


using System;
using System.Collections.Generic;
using System.Threading;

namespace E
{
    public class Person
    {
        public string Name { get; set; }
    }
    public class ResponsibleHolder
    {
        private static ResponsibleHolder instance = null;
        private static object mylock = new object();
        private Dictionary<int, Person> responsible;
        private ResponsibleHolder()
        {
            responsible = new Dictionary<int, Person>();
        }
        public static ResponsibleHolder Instance
        {
            get
            {
                lock(mylock)
                {
                    if(instance == null)
                    {
                        instance = new ResponsibleHolder();
                    }
                }
                return instance;
            }
        }
        public Person Responsible
        {
            get
            {
                lock(responsible)
                {
                    return responsible[Thread.CurrentThread.ManagedThreadId];
                }
            }
            set
            {
                lock(responsible)
                {
                    responsible[Thread.CurrentThread.ManagedThreadId] = value;
                }
            }
        }
    }
    public class Foo
    {
        public void A()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Foo.A");
            Console.WriteLine("A");
        }
        public void B()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Foo.B");
            Console.WriteLine("B");
        }
    }
    public class Bar
    {
        public void C()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Bar.C");
            Console.WriteLine("C");
        }
        public void D()
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " called Bar.D");
            Console.WriteLine("D");
        }
    }
    public class ApplicationLogic
    {
        public void DoSomethings()
        {
            ResponsibleHolder.Instance.Responsible = new Person { Name="Arne" };
            Foo foo = new Foo();
            foo.A();
            foo.B();
            Bar bar = new Bar();
            bar.C();
            bar.D();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}
Avatar billede arne_v Ekspert
20. januar 2013 - 21:50 #15
Men skal vi have pyntet paa den kaldte side bliver vi noedt til at bruge noget AOP.

Vi starter med at kombinere med singleton.

Foerste henter vi lige de kaldte ud i et eget namespace i egen source og egen dll (det er ikke absolut noedvendigt - men det separate namespace goer det lidt nemmere at select hvad man skal logge):


using System;

namespace EE
{
    public class Foo
    {
        public void A()
        {
            Console.WriteLine("A");
        }
        public void B()
        {
            Console.WriteLine("B");
        }
    }
    public class Bar
    {
        public void C()
        {
            Console.WriteLine("C");
        }
        public void D()
        {
            Console.WriteLine("D");
        }
    }
}



using System;
using System.Collections.Generic;
using System.Threading;

using EE;

namespace E
{
    public class Person
    {
        public string Name { get; set; }
    }
    public class ResponsibleHolder
    {
        private static ResponsibleHolder instance = null;
        private static object mylock = new object();
        private Dictionary<int, Person> responsible;
        private ResponsibleHolder()
        {
            responsible = new Dictionary<int, Person>();
        }
        public static ResponsibleHolder Instance
        {
            get
            {
                lock(mylock)
                {
                    if(instance == null)
                    {
                        instance = new ResponsibleHolder();
                    }
                }
                return instance;
            }
        }
        public Person Responsible
        {
            get
            {
                lock(responsible)
                {
                    return responsible[Thread.CurrentThread.ManagedThreadId];
                }
            }
            set
            {
                lock(responsible)
                {
                    responsible[Thread.CurrentThread.ManagedThreadId] = value;
                }
            }
        }
    }
    public class ApplicationLogic
    {
        public void DoSomethings()
        {
            ResponsibleHolder.Instance.Responsible = new Person { Name="Arne" };
            Foo foo = new Foo();
            foo.A();
            foo.B();
            Bar bar = new Bar();
            bar.C();
            bar.D();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}


Og saa pointen - et aspect som faar det til at virke:


using System;

using Vajhoej.StaticWeaver;

using E;

namespace EEE
{
    [Aspect]
    public class SingletonLogAspect
    {
        [Pointcut(Signature="* EE.*::*(*)")]
        public static void LogPointcut() { }
        [BeforeCallAdvice(Pointcut="LogPointcut")]
        public static void Log(MethodJoinpoint mjp)
        {
            Console.WriteLine(ResponsibleHolder.Instance.Responsible.Name + " calling " + mjp.TypeName + "." + mjp.MethodName);
        }
    }
}


Nu er logningen flyttet fra de enkelte klasser/metoder over i et enkelt sted - nemlig et aspect.
Avatar billede arne_v Ekspert
20. januar 2013 - 21:52 #16
Nu kan man stadigvaek vare lidt ulden ved den singleton og der er faktisk en alternativ maade at goere det paa ved at definere at det kaldende objekt skal have property.


using System;

using EE;

namespace E
{
    public class Person
    {
        public string Name { get; set; }
    }
    public interface IHasResponsible
    {
        Person Responsible { get; set; }
    }
    public class ApplicationLogic : IHasResponsible
    {
        public void DoSomethings()
        {
            Responsible = new Person { Name="Arne" };
            Foo foo = new Foo();
            foo.A();
            foo.B();
            Bar bar = new Bar();
            bar.C();
            bar.D();
        }
        public Person Responsible { get; set; }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            ApplicationLogic app = new ApplicationLogic();
            app.DoSomethings();
        }
    }
}



using System;

using Vajhoej.StaticWeaver;

using E;

namespace EEE
{
    [Aspect]
    public class LogAspect
    {
        [Pointcut(Signature="* EE.*::*(*)")]
        public static void LogPointcut() { }
        [BeforeCallAdvice(Pointcut="LogPointcut")]
        public static void Log(MethodJoinpoint mjp)
        {
            Console.WriteLine(((IHasResponsible)mjp.Caller).Responsible.Name + " calling " + mjp.TypeName + "." + mjp.MethodName);
        }
    }
}
Avatar billede CodingJoe Nybegynder
21. januar 2013 - 21:17 #17
Waauw!!!! Jeg bliver lige nødt til at bruge den  kommende weekend på at tygge det igennem, da jeg har en del at se til i denne uge.

Jeg er sikker på, der er masser af lækre guldkorn.

Smid bare et svar ind i mellemtiden Arne. :D
Avatar billede arne_v Ekspert
22. januar 2013 - 04:49 #18
Hvis du vil igang med AOP i .NET saa er det som sagt lidt grumset.

Der er masser af gode muligheder indenfor dynamic weavers.

Men static weavers er der langt imellem.

http://aspectdng.tigris.org/

var rigtigt godt men det virker ikke med nyere .NET versioner, forfatteren droppede projektet i 2008 og det er ikke nemt at genoplive (jeg har proevet).

http://sheepaspect.codeplex.com/

ser ret lovende ud, men det er aldrig blevet laver faerdigt og det virker ikke for godt.

http://www.vajhoej.dk/arne/opensource/yaaopf/

er hvad jeg har brugt i eksemplerne. Og det er saamaend heller perfekt, men jeg kan rette fejl og tilfoeje ny funktionalitet hvis noedvendigt.
Avatar billede CodingJoe Nybegynder
04. april 2013 - 00:46 #19
Jeg må hellere få lukket denne.
Smid et svar ind
Avatar billede arne_v Ekspert
04. april 2013 - 01:36 #20
ok
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