开发者

Modern way to declare a class in php

Wanting to make sure I'm using classes properly. The main script accepts this from the user:

1. month
2. year
3. the type of event they want to find (microsoft or linux)

For the program to find a microsoft event it has to run the regular expression: '(ID=)+[0-9]+' For the program to find a linux event it has to run the regular expression: '(ID=)+[A-F]+'

Creating an Events class seems a logical way to organize the code. I don't want to send the required regular expression into the class from the main script if possible. I want to tell the events class if it is a microsoft or linux event and let the class return the processed data based on variables that are pre-defined in the class for each type of event.

I have existing code written as:

 class Events
    {
            var $month;
            var $year;
            var $event_identifier_expression;

      public function events($month, $year)
      {
       $this->month = $month;
       $this->year = $year;
       return $this->event_identifier_expression.$month.$year;
      }
    }

I want to use something like multiple static helper methods in the Events class instead. If I change the existing code to the code below, will it allow me to call microsoft_events and linux_events independently from outside the class, and is this correct way to create the class? (example code for calling the static functions from outside the class is appreciated) :

class Events
    {
            public function __construct()
            {var $month;
             var $year;
             var $event_identifier_expression;
            } 

      public static function mi开发者_StackOverflowcrosoft_events($month, $year)
      {
       $this->month = $month;
       $this->year = $year;
       $this->event_identifier_expression = '(ID=)+[0-9]+';
       return $this->event_identifier_expression.$month.$year;
      }
      public static function linux_events($month, $year)
      {
       $this->month = $month;
       $this->year = $year;
       $this->event_identifier_expression = '(ID=)+[A-F]+';
       return $this->event_identifier_expression.$month.$year;
      }
    }


If you want to use everything statically (which makes it essentially a prettied up procedural piece of code), you'll have to do something like this:

class Events
{
   static protected $month;
   static protected $year;

   public static function microsoft_events($month, $year)
   {
      self::$month = $month;
      self::$year = $year;
   }
   public static function linux_events($month, $year)
   {
      self::$month = $month;
      self::$year = $year;
   }
}

Events::microsoft_events(12, 2010);

Note that you can only have a single instance then, every call will change the values for everything.


I would suggest a different approach to this. Having different methods for each even types will require you to write repeating code, which will eventually lead to costly maintainable code and error prone (even copy/paste is error prone! and I've seen it in some projects to prove it).

First, static calls should be limited only (or mostly) when methods does not require to store data in class members; a static method should not set anything, less data to be used by instances of the class itself. You should therefore use the singleton pattern.

Second, if you should have many event types, each should have their proper specialized class. Object oriented design goes from abstract to concrete, general to specialized. Therefore, your Events should not know how many or what event it stores, but should leave that to the caller. This will enable you to have a more coherent interface.

Here would be an example design :

class Events {

   static private $_instance;

   static public function getInstance() {
      // lazy loading of the class instance will not use unnecessary resources
      if (null === self::$_instance) {
         self::$_instance = new self();  // <-- valid PHP declaration
      }
      return self::$_instance;
   }

   // class members

   protected $_events;      // protected allows inheritance, use private to forbid it

   // private constructor prohibit external instances to be created
   private function __construct() {
      $this->_events = array();
   }

   // (GETTER: methods that starts with 'get'...)
   // use $year before $month for priority order ($month is more precise than $year)
   public function getEvents($type, $year = null, $month = null) {
      $_values = array();

      // if we have any event of that type...
      if (array_key_exists($type, $this->_events)) {
         foreach ($this->_events[$type] as $event) {
            // filter events to return... (if $year is null, $month is ignored)
            if ((null === $year 
              || (($year == $event->getYear())
              && (null === $month || $month == $event->getMonth()) )) ) {

               $_values[] = $event;
            }
         }
      }

      return $_values;
   }

   // (SETTER: methods that starts with 'add', 'set', etc.)
   public function addEvent(AbstractEvent $event) {
      if (!array_key_exists($event->getType(), $this->_events)) {
         $this->_events[$event->getType()] = array();
      }
      $this->_events[$event->getType()][] = $event;

      // returning $this allows chaining. 
      // While some argue the design of this, I personally like it
      return $this;
   }

}

Now that we have our container, we will need event types, first we have our base class :

abstract class AbstractEvent {
   protected $_year;    // again, protected to enable inheritance
   protected $_month;

   public function __construct($year, $month) {
      // TODO : perform some argument check here
      $this->_year = $year;
      $this->_month = $month;
   }

   abstract public function getType();
   public function getYear() { return $this->_year; }
   public function getMonth() { return $this->_month; }
}

Then, simply specialize it (we create two specialize event types) :

class MicrosoftEvent extends AbstractEvent {
   const TYPE = 'Microsoft';
   public function getType() { return self::TYPE; }
}

class LinuxEvent extends AbstractEvent {
   const TYPE = 'Linux';
   public function getType() { return self::TYPE; }
}

Adding new events

Events::getInstance()
  ->addEvent( new LinuxEvent(2010, 7) )
  ->addEvent( new MicrosoftEvent(2008, 8) )
  ->addEvent( new MicrosoftEvent(2010, 2) )
  ->addEvent( new LinuxEvent(2009, 1) )
  // ...
;

Get events

// 1. get ALL Microsoft events
var_export( Events::getInstance()->getEvents(MicrosoftEvent::TYPE) );
// 2. get all events for 'Linux' in 2010
var_export( Events::getInstance()->getEvents('Linux', 2010) );
// 3. same as 1; $month will be ignored, because $year is not specified
var_export( Events::getInstance()->getEvents('Microsoft', null, 6) );
// 4. returns empty array because unknown event type
var_export( Events::getInstance()->getEvents('Foo') );

As you can see, the overhead for class design is a little more complex, but the API is a lot more consistent afterwards. A good design must apply the reusable pattern and this is accomplished here. Hope this helps.

******EDIT**** since your question has changed, here is an edited solution. It is much smaller, but still follow the same base design :

class Events {

   static private $_events = array();

   // GETTER
   static public function getEventType($type) {
      // if we have any event of that type...
      if (!array_key_exists($type, self::$_events)) {
         $eventClass = $type . 'Event';
         self::$_events[$type] = new $eventClass();
      }

      return self::$_events[$type];
   }

}

Then our base event type class

abstract class AbstractEvent {
   abstract public function getType();
   public function getIdentifier($year, $month) {
      return $this->getType().str_pad((int) $month, 2, '0', STR_PAD_LEFT).str_pad((int) $year, 4, '0', STR_PAD_LEFT);
   }
}

Now, we specialize the types

class MicrosoftEvent extends AbstractEvent {
   const TYPE = 'Microsoft';
   public function getType() { return self::TYPE; }
}

class LinuxEvent extends AbstractEvent {
   const TYPE = 'Linux';
   public function getType() { return self::TYPE; }
}

Then we test the results

var_export( Events::getEventType(MicrosoftEvent::TYPE)->getIdentifier(2008, 6) );
var_export( Events::getEventType(LinuxEvent::TYPE)->getIdentifier(2010, 2) );
var_export( Events::getEventType('Microsoft')->getIdentifier('2009', '08') );


$this can't be used within a static method - one can't assume that the class will be instantiated here...

You would have to declare your class like this:

class Events
    {
        public static $month;
        public static $year;

        public function __construct(){    }

        public static function microsoft_events($month, $year)
        {
            self::month = $month;
            self::year = $year;
        }

        public static function linux_events($month, $year)
        {
            self::month = $month;
            self::year = $year;

            //do something?

            if(self::month == 'March' && self::year == '2010'){
                return 'Linux horoscope looks great';
            }
            return self::month . ' ' . self::year . ' is even better!';
        }
}

Calling:

$returnedVar = Events::linux_events('March', '2010');


Off the top of my head, i can suggest something like

/// Raw event data, e.g. database
class EventStore { 
    function getRawDataMatching($pattern) {
       // query the db
    }
 }

/// Mother of all Event types
abstract class Event {
    static function getSearchPattern() {
       // return the search pattern for the concrete event class
    }
    function __construct($rawData) {
       // initialize from the raw data
    }
}

/// Concrete event classes
class MicrosoftEvent extends Event {...}
class LinuxEvent extends Event {...}

/// Find events in the stream
class EventFinder 
{
    function __construct(EventStore $store) { ... }

    function classFor($type) {
      // return event class name for the given type
    }

    function find($date, $type) {
       $klass = $this->classFor($type);
       $pattern = $klass::getSearchPattern();
       foreach($this->store->getRawDataMatching($pattern) as $data)
            $result[] = new $klass($data)

    }
}

// main code
$store = new EventStore($connectInfo);
$finder = new EventFinder($store);
$foundEvents = $finder->find($input->date, $input->type);

This is one of many possible ways, of course. Let me know if you need further comments.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜