Καλώς ορίσατε στο dotNETZone.gr - Σύνδεση | Εγγραφή | Βοήθεια
σε

 

Αρχική σελίδα Ιστολόγια Συζητήσεις Εκθέσεις Φωτογραφιών Αρχειοθήκες

Μια υλοποίηση του FilterChain pattern ...

Îåêßíçóå áðü ôï ìÝëïò anjelinio. Τελευταία δημοσίευση από το μέλος PALLADIN στις 05-05-2007, 20:00. Υπάρχουν 19 απαντήσεις.
Σελίδα 1 από 2 (20 εγγραφές)   1 2 >
Ταξινόμηση Δημοσιεύσεων: Προηγούμενο Επόμενο
  •  28-04-2006, 20:27 12206

    Μια υλοποίηση του FilterChain pattern ...

    Συνημμένα: FilterChain.rar

    Πρόσφατα στη δουλειά χρειάστηκε να περάσω ένα DataSet από μια σειρά από «φίλτρα», ή «transformations».

    Μετά από δύο – τρείς μέρες, χρειάστηκε να περάσω ένα άλλο DataSet από μια σειρά «φίλτρα» ή «validators».

    Επίσης, πολύ συχνά μου χρειάζεται να περάσω κάθε incoming request από μια αντίστοιχη σειρά από «φίλτρα».

    Κοντολογίς, πάρα πολύ συχνά, η ίδια ιστορία. Φτιάξε τη λίστα από τα αντικείμενα-φίλτρα, γράψε το απαραίτητο for-each, και γύρνα το αποτέλεσμα στον caller …

    Τα παραπάνω περιγράφονται πολύ όμορφα στο Filter Chain pattern. Το Filter Chain, το οποίο είναι γνωστό με διάφορα ονόματα (Chain of Responsibility, Intercepting Filter κ.α.), προσωπικά το συνάντησα στα J2EE Core Patterns, όπου υπάρχει ήδη μια υλοποίηση στο spec του εκάστοτε J2EE Container.  

    Έτσι, αποφάσισα μια όμορφη μέρα να γράψω κι εγώ μια σχετικά απλή υλοποίηση. Μου προσφέρει τη δυνατότητα να ορίσω ( κλασσικά σε XML ) τα φίλτρα μιας λίστας, και από αυτή την περιγραφή να παίρνω instances τα οποία χρησιμοποιώ ανάλογα την περίπτωση …

    Ας δούμε λοιπόν.

    Το βασικό interface είναι το IFilter. Περιέχει τα απολύτως απαραίτητα. Ένα  όνομα, και μια μέθοδος η οποία δέχεται ένα object σαν παράμετρο, και επιστρέφει ένα object σαν αποτέλεσμα. Τίποτα σπουδαίο:

    namespace FilterChain
    {
           /// <summary>
           /// Summary description for IFilter.
           /// </summary>
           public interface IFilter
           {
                  /// <summary>
                  /// An identifying name for the chain
                  /// </summary>
                  string Name {get;set;}
     
                  /// <summary>
                  /// defines the method that will get called by the chain, so
                  /// as to process a given input through this filter instance
                  /// </summary>
                  /// <param name="input"></param>
                  /// <returns></returns>
                  object Proccess(object input);
           }
    }

    Το αμέσως επόμενο interface, είναι το IFilterChain. Εξίσου απλό, περιέχει ότι περιέχει και το IFilter, και προσθέτει ένα property που ορίζει τον τρόπο με τον οποίο το chain θα εκτελέσει τα φίλτρα του, και το collection με τα instances των φίλτρων:

    namespace FilterChain
    {
           /// <summary>
           /// Defines the running mode of a FilterChain:
           ///    1. Sequential will feed the output of Filter 1 to Filter 2 and so on
           ///    2. Parallel will feed all Filters with the initial input instance
           /// </summary>
           public enum FilterChainMode {
                  Sequential = 0,
                  Parallel = 1
           }
     
           /// <summary>
           /// IFilterChain defines the basic interface for a sequence of
           /// proccesses, executed on an 'input' object.
           /// </summary>
           public interface IFilterChain : IFilter
           {
                  /// <summary>
                  /// Retrieves the runing mode of the FilterChain
                  /// </summary>
                  FilterChainMode ChainMode {get;set;}
                  /// <summary>
                  /// Collections of filter instances.
                  /// </summary>
                  Filter[] Filters {get; set;}
           }
    }
     

    Υπάρχουν μόνο δύο σημειώσεις εδώ ...

    Πρώτα, το ChainMode (Sequential, Parallel) καθορίζει το πώς θα εκτελεστούν τα φίλτρα. Sequential, είναι σε σειρά. Τα επομενο φίλτρο θα εκτελεστεί, αλλα προσοχή(!) το input που θα πάρει, θα είναι το αποτέλεσμα του προηγούμενου φίλτρου στην αλυσίδα !

    Με αυτό τον τρόπο π.χ. μπορώ αν περάσω το HttpContext ενός request στην αλυσίδα, να προσθέτω items στο HttpContext.Items ή στο Session κτλ κτλ και κάθε επόμενο processing να έχει access στις αλλαγές/προσθήκες του προηγούμενου. Δεν ακούγεται σπουδαίο αλλά, πιστέψτε με, είναι ένα pattern που το βρίσκεις συνεχώς μπροστά σου.

    Το Parallel mode, δίνει ως input σε όλα φίλτρα το αρχικό input. Με αυτόν τον τρόπο, μπορώ να δημιουργήσω filter chains για validation για παράδειγμα. Αν και αυτό το σενάριο δεν είναι το σύνηθες, stadar θα σας φανεί χρήσιμο στο μέλλον … το δικό μου μυαλό σκέφτηκε ας πούμε notifications over different mediums ( email, sms, MSN Alerts κτλ. κτλ. )

    Επίσης, ένα "accidental" feature είναι οτι αφού το IFilterChain κάνει inherit το IFilter, μπορούμε να έχουμε nested filter chains :D ... πολύ χάρηκα όταν το συνειδητοποίησα αυτό, και είπα να το μοιραστώ μαζί σας :]

    Μέσα στο ίδιο class library, υπάρχουν υλοποιήσεις για το FilterChain & Filter. Ο βασικός λόγος ύπαρξής τους είναι για να κάνω enforce κάποιους κανόνες ( ότι το όνομα π.χ. δε μπορεί να είναι null ή string.Empty ), και για την υλοποίηση της βασικής λογικής του FilterChain ανάλογα με το FilterChainMode που του δίνουμε. Από ‘κεί και πέρα όμως, υλοποιούν και ένα πολύ βασικό μέρος της όλης υπόθεσης. Το parsing ενός config file, και τη δημιουργία των instances του FilterChain και των Filters του.

    Όπως πάντα, έχω χρησιμοποιήσει κατά κόρον XmlSerialization, και οι 2 κλάσεις είναι  XmlSerialization-ready. Αυτό αποδείχθηκε ευχή και κατάρα, για τους λόγους που θα αναλύσω στο τέλος αυτού του post. Ας δούμε ένα πολύ απλοϊκό παράδειγμα πρώτα …

    <?xml version="1.0" encoding="utf-8" ?>
    <test-chain id="test-chain" chain-mode="Sequential">
       
    <filters>
          
    <test-filter id="test filter 1"/>
          
    <filter id="test filter 2"/>
       
    </filters>
    </test-chain>

    Εδώ, καθορίζω ένα απλό chain, με 2 φίλτρα τα οποία θα τρέξουν σε σειρά. Το πρώτο είναι ένα custom filter, subclass του Filter, ενώ το άλλο είναι απλώς η base υλοποίηση που υπάρχει μέσα στο library και δεν κάνει στην ουσία .. τίποτα.  

    Δυστυχώς, και επειδή χρησιμοποιώ XmlSerialization, πρέπει να κάνω override το Filter[] Filters property του FilterChain, έτσι ώστε να του πώ ότι υπάρχουν διάφοροι δικοί μου τύποι οι οποίοι θα συμπεριληφθούν σαν elements μέσα στο <filters> tag.

    Κατά τα άλλα, κάνω και override το Process της base FilterChain, μόνο και μόνο για να γράφω κάτι στην κονσόλα. Δε χρειάζεται στην πραγματικότητα. Ιδού λοιπόν τα 2 sources: 

    namespace FilterChainTest
    {
           /// <summary>
           /// Summary description for TestChain.
           /// </summary>
           [XmlRoot("test-chain")]
           public class TestChain : FilterChain.FilterChain
           {
                  public TestChain() : base()
                  {
                         //
                         // TODO: Add constructor logic here
                         //
                  }
     
                 
                  [XmlArrayItem("test-filter", typeof(FilterChainTest.TestFilter)),
                   XmlArrayItem("filter", typeof(Filter))]       
                  [XmlArray("filters")]
                  public override Filter[] Filters {
                         get {
                               return base.Filters;
                         }
                         set {
                               base.Filters = value;
                         }
                  }
     
                  public override object Proccess(object input) {
                         System.Console.WriteLine(string.Format("Chain with name: {0} started.", base.Name));
                         object retVal =  base.Proccess (input);
                         System.Console.WriteLine(string.Format("Chain with name: {0} ended.", base.Name));
     
                         return retVal;
                  }
     
           }
    }
     
    namespace FilterChainTest
    {
           /// <summary>
           /// Summary description for TestFilter.
           /// </summary>
           [XmlRoot("test-filter")]
           public class TestFilter : Filter
           {
                  public TestFilter() : base()
                  {
                         //
                         // TODO: Add constructor logic here
                         //
                  }
     
                  public override object Proccess(object input) {
                         System.Console.WriteLine(string.Format("Filter with name: {0} called.", base.Name));
                         return null;
                  }
     
           }
    }
     

    Το μόνο σημείο που χρειάζεται ίσως λίγο επεξήγηση, είναι εκεί που ορίζω τους τύπους οι οποίοι αντιστοιχούν στα elements μέσα στο <filters> tag:

    [XmlArrayItem("test-filter", typeof(FilterChainTest.TestFilter)),
                   XmlArrayItem("filter", typeof(Filter))]       
                  [XmlArray("filters")]
                  public override Filter[] Filters { …

    Βασικά είναι απλό. Γράφεις ένα XmlArray attribute, το οποίο λέει στο serializer/deserializer ότι θα συναντήσει ένα element στην xml, τα περιεχόμενα του οποίου θα είναι ένα array / collection. Και πάνω απ’αυτό, σε ένα μόνο attribute ορίζουμε XmlArrayItems καθένα από τα οποία αντιστοιχεί ένα element name με ένα τύπο …  

    Οκ λοιπόν, μετά από όλα αυτά, η χρήση … 

    using System;
    using System.IO;
     
    using FC = FilterChain;
    using FilterChainTest;
     
    namespace FilterChainTest
    {
           /// <summary>
           /// Summary description for Class1.
           /// </summary>
           class MainClass
           {
                  /// <summary>
                  /// The main entry point for the application.
                  /// </summary>
                  [STAThread]
                  static void Main(string[] args)
                  {
                         try{
                               FC.IFilterChain chain = FC.FilterChain.FromFile(Path.Combine(System.Environment.CurrentDirectory, "../../resources/test-chain.xml"), typeof(TestChain));
                               chain.Proccess("test");
     
                               Console.ReadLine();
                         }catch(Exception e){
                               throw;
                         }
                  }
           }
    }
     

    Τίποτα σπουδαίο. Σε 3 γραμμές έχεις κάνει τη δουλειά σου πολύ όμορφα – νομίζω :P

    Υ.Γ. … κι επανέρχομαι στο θέμα του XmlSerialization. Όπως αποδεικνύεται, αν στη base κλάση σου έχεις ορίσει ένα property με XmlArray, δεν μπορείς απλώς στα subclasses να προσθέσεις XmlArrayItems … ο Deserializer βλέπει μόνο αυτά της μαμάς κλάσης, και αγνοεί τελείως ( μα τελείως !;!;!;) τα δικά σου …  Οπότε, αναγκαστικά έκανα μια ολόκληρη .. χακιά να την πώ; Μπακαλιά; … δεν ξέρω τι, αλλά ορίζω και το XmlArray και τα XmlArrayItems στη subclass, και πρίν κάνω deserialize, σκανάρω τον τύπο του FilterChain το οποίο θέλω να κάνω deserialize, διαβάζω τα XmlSerialization attributes του, και τα ταϊζω στον XmlSerializer, κάνοντας έτσι override τα defaults, και παίρνω πίσω ολόκληρο και πανέμορφο το collection με τα φίλτρα του chain μου … Όλα αυτά γίνονται στην FromFile(…)  ( static method η οποία δημιουργεί ένα chain από ένα xml αρχείο ):

    /// <summary>
                  /// Creates the required custom XmlArrayItemAttribute attributes in an
                  /// XmlAttributeOverrides instance, so as to properly deserialize the
                  /// Filter instances for the given FilterChain Type.
                  /// </summary>
                  /// <param name="chainType"></param>
                  /// <returns></returns>
                  private static XmlAttributeOverrides CreateAttributes(Type chainType) {
                         // ok, build the XmlAttributeOverrides that we'll return ..
                         XmlAttributeOverrides overrides = new XmlAttributeOverrides();
     
                         XmlAttributes attrs = new XmlAttributes();
                         attrs.XmlArray = new XmlArrayAttribute("filters");
                         // Get the property info, so as to rip the XmlArrayItemAttributes off that ...
                         PropertyInfo prop = chainType.GetProperty("Filters");
                         XmlArrayItemAttribute[] propAttrs = (XmlArrayItemAttribute[])prop.GetCustomAttributes(typeof(XmlArrayItemAttribute), false);
                         // add those XmlArrayItem attributes to the collection
                         foreach(XmlArrayItemAttribute propAttr in propAttrs)
                               attrs.XmlArrayItems.Add(propAttr);
                         // and now .. override the BASE CLASS's attributes on the Filters property !!!
                         overrides.Add(typeof(FilterChain), "Filters", attrs);
                         // return the new attribute collection
                         return overrides;
                  }
     
                  /// <summary>
                  /// Instantiates a given IFilterChain, from an XML file.
                  /// </summary>
                  /// <param name="filePath"></param>
                  /// <param name="chainType"></param>
                  /// <returns></returns>
                  public static IFilterChain FromFile(string filePath, Type chainType){
                         if(File.Exists(filePath)){
                               FileStream ioIn = File.OpenRead(filePath);
                               try {
                                      return (IFilterChain)(new XmlSerializer(chainType, CreateAttributes(chainType))).Deserialize(ioIn);
                               }finally {
                                      ioIn.Close();
                               }
                         }else
                               throw new ArgumentException(string.Format("File {0} does not exist", filePath), "filePath");
                  }

    Με αυτά και μ’αυτά … πάει 8 και η ώρα, και πρέπει να μας αφήσω :D Happy chainin’ !!!

     


    Angel
    O:]
  •  30-05-2006, 11:34 13473 σε απάντηση της 12206

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Συνημμένα: FilterChain_v2.rar
    Συνεχίζοντας τις Filter Chained αναζητήσεις μου, σκεφτόμουν το βασικό pattern ... μια σειρά  απο instances κλάσεων οι οποίες παίρνουν σαν παράμετρο ένα object ... και επιστρέφουν ένα ... object.

    Και τότε μου ήρθε ... δε θα ήταν πολύ όμορφο να έχω typed παραμέτρους στα φίλτρα μου; Κι ακόμη περισότερο ... να έχω typed φιλτρα, τα οποία όμως επιστρέφουν διαφορετικούς τύπους, και να παίρνουν παραμέτρους διαφορετικών τύπων !;!;!;! Έτσι θα μπορούσα να υλοποιήσω στην ουσία ... orchestration και workflow λογική.

    Έτσι λοιπόν, κάθησα να το δώ λίγο πιο εκτενώς το θέμα ... αποτέλεσμα, η κλάσση TypedFilter. Η δουλειά της είναι να παρέχει τη βασική υλοποίηση του Filter, δηλαδή τη μέθοδο object Proccess(object input) και να καλεί εσωτερικά μια δικιά μου μέθοδο, με typed παραμέτρους.

                  public sealed override object Proccess(object input) {
                         return this.TypedMethod.Invoke(this, new object[1]{input});
                 
    }

      Το επόμενο ερώτημα ήταν ... "και πώς θα ξέρει τώρα αυτή ποιά είναι η μέθοδος την οποία πρέπει να καλέσει;;;". Ο μόνος περιορισμός που ήθελα, ήταν στον αριθμό των παραμέτρων που δέχεται η typed μεθοδος, ο οποίος θα πρέπει να είναι ... μια. Στο φτωχό μυαλό μου αυτό promot-άρει το encapsulation των παραμέτρων σε ένα struct ή class, το οποίο προσωπικά μου αρέσει σαν concept. Σε βάζει σε μια "σφιχτή" ΟΟΡ λογική, και σου δίνει και κάποια data objects για να τα χρησιμοποιείς στο μέλλον .. άλλα βέβαια αυτά είναι προσωπικές προτιμήσεις.

    Τέλος πάντων. Το βασικό ερώτημα ήταν πως θα ξέρω ποιά μέθοδο να καλέσω. Η απάντηση; Custom Attribute:

    using System;

     

    namespace FilterChain

    {

           /// <summary>

           /// TypedPropertyAttribute indicates a filter method that

           /// will be called to provide typed handling of input for

           /// filters in a filter chain.

           /// </summary>

           [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]

           public class TypedMethodAttribute : Attribute

           {

                  private string m_typedMethodName = string.Empty;

     

                  public TypedMethodAttribute() : base()

                  {

                        

                  }

     

                  /// <summary>

                  /// Indicates the method that will be called to provide typed input

                  /// </summary>

                  public string TypedMethodName {

                         get {

                               return m_typedMethodName;

                         }

                         set {

                               if(null==value || string.Empty.Equals(value))

                                      throw new ArgumentNullException("TypedMethodName", "Is Nul or Empty");

     

                               m_typedMethodName = value;

                         }

                  }

           }

    }

    Πολύ-πολύ απλό το attribute. Απλώς κρατάει το όνομα της μεθόδου που θα κληθεί. Με λίγο caching δεν έχει και τρελλό performance overhead, και μου έδωσε τη λύση που ήθελα, μια χαρά :]

    Ας δούμε κι ένα παραδειγματάκι. Η sample εφαρμογούλα που έχω attached δεν κάνει κάτι σπουδαίο ( βασικά ... τίποτα δεν κάνει :D ). Έχει τρία φίλτρα, τα οποία απλώς παίζουν μ'ενα αρχικό string που παίρνει σαν παράμετρο το πρώτο, το κάνουν int στο δεύτερο, και πάλι string στο τρίτο. Απλώς δείχνει πως μπορείς να "δέσεις" μέσα σε ένα Filter Chain τρείς κλάσεις οι οποίες δέχονται και επιστρέφουν διαφορετικούς τύπους η μία στην άλλη, κάνοντας έτσι το μικρό σου orchestration :)

    Παραθέτω τον κώδικα των φίτλρων:

    using System;

    using System.Xml.Serialization;

     

    using FilterChain;

     

    namespace FilterChainTest

    {

           /// <summary>

           /// Summary description for TestFilter.

           /// </summary>

           [XmlRoot("test-filter")]

           [TypedMethodAttribute(TypedMethodName="ProccessTyped")]

           public class TestFilter : TypedFilter

           {

                  public TestFilter() : base()

                  {

                        

                  }

     

                  public string ProccessTyped(string input) {

                         System.Console.WriteLine(string.Format("Filter with name: {0} called. input: {1}", base.Name, input));

                         return (input +=string.Format("\n Filter with name: {0} called.", base.Name));

                  }

     

           }

     

           /// <summary>

           /// Summary description for TestFilter.

           /// </summary>

           [XmlRoot("test-filter1")]

           [TypedMethodAttribute(TypedMethodName="ProccessTyped")]

           public class TestFilter1 : TypedFilter {

                  public TestFilter1() : base() {

                        

                  }

     

                  public int ProccessTyped(string input) {

                         System.Console.WriteLine(string.Format("Filter with name: {0} called. input: {1}", base.Name, input));

                         return input.GetHashCode();

                  }

     

           }

     

           /// <summary>

           /// Summary description for TestFilter.

           /// </summary>

           [XmlRoot("test-filter2")]

           [TypedMethodAttribute(TypedMethodName="ProccessTyped")]

           public class TestFilter2 : TypedFilter {

                  public TestFilter2() : base() {

                        

                  }

     

                  public string ProccessTyped(int input) {

                         System.Console.WriteLine(string.Format("Filter with name: {0} called. input: {1}", base.Name, input));

                         return string.Format("{0:#####.00}", input);

                  }

     

           }

    }

    Το ότι έχω ονομάσει την typed μεθοδό μου ProcessTyped και στις τρείς υλοποιήσεις είναι ... accidental. Δεν υπάρχει τέτοιο requirement, απλώς βόλευε στο copy-paste :D

    To XML definition του chain που προκύπτει είναι εξίσου απλό ...

    <?xml version="1.0" encoding="utf-8" ?>

    <test-chain id="test-chain" chain-mode="Sequential">

    <filters>

           <test-filter id="test filter "/>

           <test-filter1 id="test filter 1"/>

           <test-filter2 id="test filter 2"/>

    </filters>

    </test-chain>

    Και το output:

    Chain with name: test-chain started.
    Filter with name: test filter  called.
    Filter with name: test filter 1 called
     Filter with name: test filter  called
    Filter with name: test filter 2 called
    Chain with name: test-chain ended.
    Processed and returned: 1218466173,00
      
    To εμβόλιμο "Filter with name: test filter  called" στην 4η γραμμή είναι απλώς το input string που πήρε το δεύτερο φίλτρο στη σειρά, αν κι εμένα στην αρχή μου φάνηκε οτι κάτι πήγαινε στραβά στη σειρά εκτέλεσης των φίλτρων :P Έσφαλα όμως, και σύντομα επανήλθε η αυτοπεποίθησή μου στη μικρή μου φιλτραλυσίδα :D

    Αυτές τις μέρες το χρησιμοποιώ για την υλοποίηση ενός μικρού instrumentation το οποίο μας βγάζει exports σε PDF και φαίνεται να μας λύνει τα χέρια σε πολλά τέτοια μικρά θέματα, οδηγώντας μας σε patternization των features της εφαρμογής μας :)

    Ρίξτε μια ματιά στο νέο attached solution .. και ... happy filter-orchestrating !!!

    Angel
    O:]
  •  25-09-2006, 12:06 17140 σε απάντηση της 13473

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Η όλη ίστορια με τα φίλτρα θυμίζει πολύ functional programming στην flat μορφή:
    T f(T x). Μήπως πρέπει να αλλάξεις τον τρόπο που αντιμετωπίζεις τα φίλτρα δαν εννοιες?
    Πχ το filter chain στην πραφματικοτητα ειναι και αυτό filter αλλα composite οποτε
    T fog ( T x) == T f(g(T x)).
    Οπότε ισως μια κλαση να έκανε δουλειά και να το γύριζες σε αναδρομικούς ορισμους...
    ισως να ξεφορτωνοσουν και το array με αυτο τον τροπο...

    Έχω την εντυπωση οτι μπορούν να γινουν περισσοτερα πραγματα έτσι οπως πχ
    *ένα thread για καθε filter με τα data να έρχονται σε stream... (dataflow)?
    *load balance μεταξυ φιλτρων.
    *Ένα φίλτρο να δέχεται το αποτέλεσμα 2 άλλων (οχι filter chain αλλα filter sorted graph ή και filter graph)

    μια ιδέα μονο. ;)

    ->Hail Eris All Hail Discordia<-
  •  25-09-2006, 18:44 17158 σε απάντηση της 17140

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Wow ... σιγά σιγά ... information overload !!! Προτού αρχίσουμε να μιλάμε σε επίπεδο τελειόφοιτου post doc, έχω απορίες !!!

    1. οκ .. συγχώρεσέ μου την πιθανή άγνοια .. αλλά δεν έβγαλα άκρη με τους τύπους ! :D ( Πάνε χρόνια που έφυγα απ'το πανεπιστήμιο ... και δεν συμπαθούσα ποτέ τον dirjkstra ή όπως γράφεται τελικά το όνομά του )

    2. Όσον αφορά την αναδρομή ... αυτό δεν προϋποθέτει οτι μια κλάση κάνει τη δουλειά;; Τα οποιαδήποτε φίλτρα δεν κάνουν την ίδια δουλειά, πιθανώς δεν είναι καν subclasses μιας κοινής parent κλάσσης αν το δούμε και "by the book", απλώς κάνουν implement το IFilter.

    3. KISS .. Keep It Simple. Όπως έλεγα και τότε ... αυτή είναι απλώς μια εύκολη υλοποίηση του filter chain. Γιατί να μπλέξω τους juniors μου ( και τον εαυτό μου ) με recursion και έννοιες που πιθανώς δεν καταλαβαίνουμε πλήρως, ή το δεδομένο σενάριο χρήσης δεν το απαιτεί στην τελική ... validation ενός business object με recursion ;;; Γιατί ;;;

    Αν η προτεινόμενη χρήση ήταν κάτι που όντως απαιτούσε περισσότερα, ο σχεδιασμός σαφώς θα ήταν διαφορετικός. Παρόλα αυτά, με κέντρισες φίλε, και θα χαρώ να ακούσω περισσότερα, ίσως βέβαια σε πιο απλή γλώσσα ;)
    Angel
    O:]
  •  26-09-2006, 10:13 17209 σε απάντηση της 17158

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Ίσως σε τρόμαξα αλλα μιλάω για κάτι απλό...

    Sorry επεξηγήσεις

    1 f,g συναρτησεις και fog η σύνθεση τους δες το μαθηματικά...

    2.Ok είχα στο μυαλό μου c++ constructs, Βασικά σκεφτόμουνα μια abstract class

    ή ένα interface στην περιπτωση σου πες Filter που οριζει μια συναρτηση T proc(T).

    καθώς και έχει ένα δείκτη στο επόμενο φίλτρο από construction πες. οπότε μόλις τελείωσει το local_process κάθε φίλτρο ελέγχει αν υπάρχει επόμενο, αν υπάρχει καλέι την δικιά του process και επιστρέφει το τελικό αποτέλεσμα... (η αναδρομή που λέγαμε) πχ σε c++ με virtual functions...

    class Filter_base {

    private: Filter_base * _next;

    public:

    Filter_base(Filter_base *next=0):_next(next) {}

    void *operator ()(void *d)

    { return _next ? _next->operator()(proc(d)) : proc(d); };

    virtual void *proc(void*d) { return d;}

    }

    class Myfilter1 :public Filter_base {

    Myfilter1 (Filter_base *next=0):Filter_base(next){};

    void *proc(void*d) { ...;}

    }

    class Myfilter2 :public Filter_base {

    Myfilter1 (Filter_base *next=0):Filter_base(next){};

    void *proc(void*d) { ...;}

    }



    ....

    Filter_base * my_filter_chain = new Myfilter1(new Myfilter2(new MyfilterN)));

    data = my_filter_chain(data) ;

    Αυτα με virtuals...

    Αν χρησιμοποιησεις templates με στατικό πολυμορφισμό ομως η λύση είναι πιο όμορφη ... :)

    Τώρα σε C# ( που δεν ξερω... :) ) φανταζομαι οτι κάτι παρομοιο μπορεί να γίνει

    και με λίγο generics που άκουσα οτι έχει θα έχεις και type safety αντι για void * η objects...

    Αλλά νομίζω έγινα κατανοητός...


    ->Hail Eris All Hail Discordia<-
  •  26-09-2006, 11:44 17224 σε απάντηση της 17209

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Νομίζω οτι κατάλαβα τι θέλεις να πείς αγαπητέ, αλλά ...

    Η λύση σου είναι βαθιά ριζωμένη στην κληρονομιά της C++. Η συγκεκριμένη είναι ένα linked list on crack. Data (the list & the navigation) & processing logic παρεούλα μέσα σε κάθε φίλτρο. Το νόημα του pattern ΕΙΝΑΙ το array, κι ακόμη περισσότερο ο declarative τρόπος να ορίσεις αυτό το array, δημιουργώντας έτσι νέα filter chain implementations δίχως μια γραμμή κώδικα. Νομίζω οτι είναι πολύ ωραίο να ασχολείσαι με lower-level πράγματα απο το .NET CLR ή το Java JVM, αρκεί να μην αφήνεις το εργαλείο σου να καθορίζει τους ορίζοντές σου - πράγμα που φοβάμαι οτι συμβαίνει με τους C/C++ προγραμματιστές, πολλοί απ'τους οποίους κουβαλάνε στην πλάτη τους νοοτροπίες "κλειστές" οι οποίες εκλείπουν στο νέο, managed, programmer-frienldy κόσμο.

    Η υλοποίηση του στύλ Filter_base * my_filter_chain = new Myfilter1(new Myfilter2(new MyfilterN))); όπου θα πρέπει κάποιος να   γ ρ ά ψ ε ι  αυτόν τον constructor - με συγχωρείς για αυτό που θα πώ - μου φαίνεται απλώς .. ακαλαίσθητη. Η συγκεκριμένη γραμμή κώδικα είναι κάτι που ποτέ δε θα ήθελα να επιβάλλω σε κάποιον να γράψει. Υπάρχει implementation με αυτόν ακριβώς τον τρόπο και σε Java, αλλά απλώς σχεδόν αποκλείει κάθε περίπτωση "δυναμισμού" @ runtime. Ακόμη και με generics - ευχαριστώ πολύ τη C++ και τα templates που έδειξαν το δρόμο προς τα 'κει κατα κάποιο τρόπο.

    Αν θές να το δείς και αλλιώς, η δουλειά του φίλτρου δεν είναι να ξέρει ή να ελέγχει με τον οποιονδήποτε τρόπο το "navigation" του chain. Η δουλειά του είναι να πάρει ένα input, και να δώσει πίσω ένα output and that's that. Δεν είναι δικό του concern - αν θές να χρησιμοποιήσουμε και λίγο AOP ορολογία - να  ξέρει οτιδήποτε  πέραν απο  οτι χρειάζεται 
    α π ό λ υ τ α  να ξέρει. Κι αυτό είναι μόνο το input του. Σκέψου, ότι αν το φίλτρο ξέρει μόνο το επόμενο node προς εκτέλεσην, τότε χάνεις κάθε έννοια reusability του φίλτρου. Τί εννοώ ... έχω ένα φίλτρο υλοποιημένο με τον linked list τρόπο που έδωσες. Sequential. Αν εγώ θελήσω να χρησιμοποιήσω το ίδιο φίλτρο σε ένα parallel implentation του chain ... έχασα. Θα πρέπει να κάνω κάποια χακιά, όπως το να μήν του περάσω pointer στο επόμενο φίλτρο, και κάπως ( πιθανότατα .. στο filter chain class ! ) να κανονίσω το navigation εξωτερικά του φίλτρου. Άρα ... επιστρέφουμε κατα κάποιο τρόπο στο array και το εξωτερικό του φίλτρου navigation.

    Όσον αφορά τις δυνατότητες πέρα απο τη στενή συζήτηση  του αν  ένα linked list είναι πιο όμορφη απο ένα array, δεν υπάρχει κανένας περιορισμός. Μόνο στη φαντασία του implementer. Γιατί δε μπορείς να ξεκινήσεις τα φίλτρα σε διαφορετικά threads ? Γιατί δεν μπορείς και έτσι να τα κάνεις load-balanced ? Τα πάντα είναι ένα override στην process(...) του filter chain που θές να φτιάξεις.

    Τώρα, όσον αφορά το ότι ένα array ή ένα linked list δεν ανοίγει τόσες προοπτικές όσο ένα graph ... έχεις δίκιο. Αλλά έτσι είναι το pattern, και υπάρχει για συγκεκριμένους σκοπούς. Αν χρειαζόταν να φτάσω σε graphs ... θα χρησιμοποιούσα κάποιο έτοιμο workflow πακέτο, που σίγουρα θα δώσει κάτι ακόμη παραπάνω. Δεν υπάρχει λόγος να φτιάξω ένα αεροπλανοφόρο για να ρυμουλκήσω μια μαούνα στον Ισθμό της Κορίνθου :D

    Αυτά γι'αυτο ... με συγχωρείς αν παρασύρθηκα ή πορώθηκα ή ίσως έγραψα οτιδήποτε που μπορεί να φανεί προσβλητικό. Δεν ήταν η πρόθεσή μου.

    Άντε, καλημέρα μας !!!

    Angel
    O:]
  •  26-09-2006, 14:48 17233 σε απάντηση της 17224

    Απ: Μια υλοποίηση του FilterChain pattern ...

    :)

    Forget Navigation αυτο ειναι implementation specific...

    Το όλο νοημα που θελω να καταλάβεις είναι οτι δεν χρειάζεται η έννοια "filter chain" αλλά μόνο η έννοια "filter" και αυτό δεν έχει να κάνει με την γλώσσα...

    Δές το απλά: τι είναι πραγματικά ένα "filter chain" αν όχι ενα composite filter...

    Δημιουργόντας την έννοια "filter chain" χρειάζεσαι και την έννοια navigation

    εγώ δεν ανέφερα ποτέ τον όρο navigation αλλα *composition* αυτό το επιτυγχανεις είτε με array ειτε με list δεν εχει σημασία δεν φαίνεται απέξω.

    Αν θες syntactic sugar σε c++ μπορεις να γραφεις και κατι σαν και αυτο:

    data = (Myfilter1,Myfilter2,MyfilterN)(data);

    αν και θεωρώ πιο ωραίο το

    data = Myfliter1(Myfilter2(MylilterN(data)));

    αν δεν θες να ξαναχρησιμοποιησεις το composition που έφτιαξες...

    Δεν καταλαβαίνω που ειναι το προβλήμα...?

    Όσο για το parallel implementation του chain αυτο που αναφερεις ειναι μαλλόν

    abuse δεν έχεις *chain* πια αλλα ενα array από φίλτρα ένα MISD construct δηλαδή που μάλλον για χακία μου φέρνει. Αν ήθελες να το κάνεις κανονικά πρέπει να πάς σε δεντρο από filters πχ σε c++ θα το δήλωνες κάπως έτσι

    data_array = tee(Myfilter1,tee(Myfilter2,MyfilterN)(data);

    με την tee να είναι φίλτρο που επιστρέφει 2 αντίγραφα της εισόδου...

    Όσο για το threading μιλάμε ουσιαστικά για βαριά φιλτρα με data που έρχονται σε stream με το filter_chain δεν ξέρω κατα πόσο ειναι εύκολο γιατι η κλάση σου θα πρέπει να διαχειριστεί όλη την επικοινωνία και τον συγχρονισμό όλων των φιλτρών ενώ αυτο που θές ειναι να επικοινωνούν και να συχρονίζονται μόνο οι γείτονες μεταξύ τους.

    Άσε με function composition μπορεις να κάνεις και ωραίο optimization

    ( ok at least for c++ )

    Anyway ποτέ δεν είπα κάντο έτσι. Απλά το ανέφερα μήπως αν το σκεφτείς διαφορετικά βολευτείς καλύτερα. Πχ ακόμα και στα φίλτρα του unix δεν υπάρχει

    κάποιος manager για το πως θα εκτελεστούνε, το κέλυφος σετάρει τα κανάλια και αφήνει τα φίλτρα ήσυχα να δουλέψουν.

     

    PS ( κακία :P ) Η C++ υποστηρίζει ήδη το functional programming paradigm χωρις τα blinqy-plinqy που θα βρείτε μπροστά σας και ξανα-ανακαλυψετε τον τροχο. Και programmer-friendly δεν σημαινει programmer-stupid.


    ->Hail Eris All Hail Discordia<-
  •  26-09-2006, 16:53 17242 σε απάντηση της 17233

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Δεν μπορώ να το διαβάσω όλο γιατί είμαι θολωμένος.

    Ρίξε όμως μια ματιά και στο decorator pattern. Μπορεί να σου δώσει κανα δυο ιδέες.


    Simple Photography
  •  26-09-2006, 17:55 17244 σε απάντηση της 17242

    Απ: Μια υλοποίηση του FilterChain pattern ...

    thAAAnos, η έννοια filter chain δεν είναι κάτι που μου ήρθε μια μέρα :) Είναι γνωστό design pattern, του οποίου ήθελα ένα implementation για να βοηθήσω τον εαυτό μου και τους απο κάτω μου. Η παρούσα υλοποίηση μας βολεύει απίστευτα τους τελευταίους μήνες, και δε βρέθηκα ακόμη μπροστά στην ανάγκη να την αλλάξω, ή να την ξανασκεφτώ απο τότε ( είναι πολύ παλιό το post ).

    Για τη δική μου περίπτωση, το chain δεν είναι απλώς ένα composite filter. Αυτό ήταν το σχεδόν accidental side-effect - κατα βάση είναι ο manager ο οποίος ορίζει το πως θα εκτελεστούν τα φίλτρα. Με νοιάζει και πάρα πολύ μάλιστα το navigation να είναι εξωτερικό του φίλτρου.

    Δεν θα είχα καμμία διαφωνία να περνάω pointers στους γείτονες του σε κάθε φίλτρο, αλλά δε με βολεύει. Δε θέλω να γράφω εγώ ή κάποιος άλλος τίποτα παραπάνω απο ένα FilterChain.FromFile(...).Process(...);

    Θα μπορούσα κάλλιστα να είχα χρησιμοποιήσει C# delegates σαν ένα είδος function pointer, αλλά έτσι μόνο θα δυσκόλευα τον κώδικα του μικρού μου junior που γράφει VB - άσε που ίσως και να μην κέρδιζα και τίποτα απο performance.



    Οπότε οι δικοί μου στόχοι είναι simplicity, ease of use, declarative interface, reuse. Ότι άλλο προκύψει, θα αλλάξει κάτω απ'το ίδιο facade, και κάτω απ'το ίδιο API.

    Κατα βάση πιστεύω οτι η διαφορά στις απόψεις μας, προέρχεται απο τους διαφορετικούς χώρους στους οποίους κινούμαστε, και η οπτική απο την οποία κοιτάμε τα πράγματα ο καθένας στον τομέα της πληροφορικής με τον οποίο ασχολείται. Απλώς οι δύο απόψεις μας .. δεν λύνουν το ίδιο πρόβλημα.

    dimcasta .. ο decorator δεν είναι filter chain και αντίστροφα, αν και μοιάζουν κ α τ α π λ η κ τ ι κ α. Βασική διαφορά είναι οτι ο decorator "decorates" ένα αντικείμενο, και συνήθως πολλοί decorators ενεργούν στο ίδιο αντικείμενο. Το ίδιο δεν ισχύει στο chain, όπου μπορεί τα return & input types μεταξύ φίλτρων να διαφέρουν. Τέλος, ο decorator δεν έχει πολλές φορές καν return type. It decorates το input του, και μάλιστα με ποικίλλους τρόπους. π.χ. ένας ui decorator θα μπορούσε να κάνει attach σε κάποια events μιας σελίδας, για να προσθαφερεί controls .. ή να κάνει validation, ή , ή , ή ... τέλος πάντων ... εγώ το chain ήθελα :D

    thAAAnos .. κερνάω καφέ φίλε οποιαδήποτε στιγμή !!! :)
    Angel
    O:]
  •  27-09-2006, 10:21 17284 σε απάντηση της 17244

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Χμμ...

    Απλά ξέρεις τι; Μάλλον αυτό που τελικα με παραξένεψε είναι όχι πως υλοποιείς το pattern. Αλλα ή χρήση του στην συγκεκριμένη περιπτωση... Έχω την εντύπωση πως το filter chain (αν έχω σωστά την αναλογία στο μυαλό μου με το chail of responsibility ) δέν είναι για να στέλνεις data από μέσα του αλλα events (requests,interupts,errors)... -Και υπό αυτήν την έννοια το πιο γνωστο παραδειγμα ειναι τα διαδοχικά catch blocks μετά το try ( χωρις το δυναμισμό κλπ (αν και με reflection... χμ να μια ιδέα! ) ).- Επιχειρώντας να στείλεις ένα dataset ( για validation/transformation )  εχω την εντυπωση οτι επιχειρείς να στήσεις ενα dataflow γράφο, εξου και η παρότρυνση μου για functional orientation του προβληματός σου...

    Αλλά από την άλλη σε βλέπω πολύ ζεστό και αυτό σημαίνει οτι σου έχει λύσει τα χέρια, τόσο που έκατσες και να το μοιραστείς μαζί μας... :) όποτε σημφωνα με την design-comandment "Never fix what ain' broken" το αφήνω εδώ.

    PS μήπως έχεις ανακαλύψει στην τελική ενα νέο pattern?

     


    ->Hail Eris All Hail Discordia<-
  •  27-09-2006, 12:27 17306 σε απάντηση της 17284

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Η αλήθεια είναι οτι η αρχική παρότρυνση για το συγκεκριμένο implementation ήταν το αντίστοιχο chain που δίνει ο εκάστοτε J2EE container ( π.χ. το filter chain του tomcat, όπου δηλώνεις κάπως αντίστοιχα στο config αρχείο σε xml τα φίλτρα απ'τα οποία θα περάσει το request ), αλλά στην πορεία το έχω χρησιμοποιήσει για χίλιους-δύο λόγους, και όντως λύνει χέρια.

    Αλλά απ'την άλλη, σκέφτομαι κι εγώ τώρα οτι ίσως πρέπει να δώ και τις άλλες απόψεις / οπτικές .. ποιός ξέρει τι μπορεί να προκύψει ;;; Γι'αυτό σίγουρα κάποια στιγμή θα κεράσω καφέ αν θές, να δούμε και την άλλη πλευρά, άλλες ιδέες ... τί geeks είμαστε στην τελική αν δεν μιλήσουμε και λίγο πιο φιλοσοφικά με τετραδιάκια και laptops στην καφετέρια ;;; :D ( χαχαχχαα ... χριστέ μου, τι καμένος είμαι !!! :D )

    Άντε, καλημέρα μας :D
    Angel
    O:]
  •  27-09-2006, 12:44 17313 σε απάντηση της 17306

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Ή μπορείτε να τα κουβεντιάσετε μπροστά σε κοινό, στα events του community που λέμε να οργανώσουμε Wink
    Vir prudens non contra ventum mingit
  •  27-09-2006, 14:51 17333 σε απάντηση της 17313

    Απ: Μια υλοποίηση του FilterChain pattern ...

    ... αν έχει και καφέ ... :D ... lol !!! :D

    Σοβαρά τώρα, αν θέλει κι ο thAAAnos νομίζω θα ήταν πολύ ενδιαφέρουσα συζήτηση.

    Angel
    O:]
  •  27-09-2006, 15:00 17334 σε απάντηση της 17333

    Απ: Μια υλοποίηση του FilterChain pattern ...

    Blue Mountain! Σου κάνει; Big Smile
    Vir prudens non contra ventum mingit
  •  27-09-2006, 17:20 17356 σε απάντηση της 17334

    Απ: Μια υλοποίηση του FilterChain pattern ...

    [ ... τί, δεν έχει ... java ??? :D ]

    Angel
    O:]
Σελίδα 1 από 2 (20 εγγραφές)   1 2 >
Προβολή Τροφοδοσίας RSS με μορφή XML
Με χρήση του Community Server (Commercial Edition), από την Telligent Systems