'분류 전체보기'에 해당되는 글 539건

  1. 2003.08.02 Pull Up Field
  2. 2003.08.02 Pull Up Constructor Body
  3. 2003.08.02 Preserve Whole Object
  4. 2003.08.02 Parameterize Method <IMG SRC = "http://www.refactoring.com/catalog/updated.gif" border=0>
  5. 2003.08.02 Move Method <IMG SRC = "http://www.refactoring.com/catalog/updated.gif" border=0>
  6. 2003.08.02 Move Field
  7. 2003.08.02 Move Class (by Gerard M. Davison) <IMG SRC = "http://www.refactoring.com/catalog/new.gif" border=0>
  8. 2003.08.02 Move Business Logic to Session (Link only)
  9. 2003.08.02 Merge Session Beans (Link only)
  10. 2003.08.02 Localize Disparate Logic (Link only)
  11. 2003.08.02 Introduce Synchronizer Token (Link only)-냉무-
  12. 2003.08.02 Introduce Parameter Object <IMG SRC = "http://www.refactoring.com/catalog/updated.gif" border=0>
  13. 2003.08.02 Introduce Null Object
  14. 2003.08.02 Introduce Local Extension <IMG SRC = "http://www.refactoring.com/catalog/updated.gif" border=0>
  15. 2003.08.02 Introduce Foreign Method
  16. 2003.08.02 Introduce Explaining Variable
  17. 2003.08.02 Introduce Business Delegate (Link only)
  18. 2003.08.02 Introduce Assertion
  19. 2003.08.02 Introduce A Controller (Link only)
  20. 2003.08.02 Inline Temp
  21. 2003.08.02 Inline Method
  22. 2003.08.02 Inline Class
  23. 2003.08.02 Hide presentation tier-specific details from the business tier (Link only)
  24. 2003.08.02 Hide Method
  25. 2003.08.02 Hide Delegate
  26. 2003.08.02 Form Template Method
  27. 2003.08.02 Extract Superclass
  28. 2003.08.02 Extract Subclass
  29. 2003.08.02 Extract Package (by Gerard M. Davison) <IMG SRC = "http://www.refactoring.com/catalog/new.gif" border=0>
  30. 2003.07.29 Extract Method

Pull Up Field


Two subclasses have the same field.

Move the field to the superclass.



For more information see page 320 of Refactoring




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
Posted by 아름프로

Pull Up Constructor Body


You have constructors on subclasses with mostly identical bodies.

Create a superclass constructor; call this from the subclass methods.


class Manager extends Employee...
        public Manager (String name, String id, int grade) {
                _name = name;
                _id = id;
                _grade = grade;
        }


        public Manager (String name, String id, int grade) {
                super (name, id);
                _grade = grade;
        }


For more information see page 325 of Refactoring




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
Posted by 아름프로

Preserve Whole Object


You are getting several values from an object and passing these values as parameters in a method call.

Send the whole object instead.


                int low = daysTempRange().getLow();
                int high = daysTempRange().getHigh();
                withinPlan = plan.withinRange(low, high);
                            


                withinPlan = plan.withinRange(daysTempRange());

For more information see page 288 of Refactoring




***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
Posted by 아름프로

Parameterize Method


Several methods do similar things but with different values contained in the method body.

Create one method that uses a parameter for the different values.



Corrections


Clarifying the Mechanics


I've since (after a suggestion from Prof Kahlbrandt) found better mechanics for this refactoring (changes are in bold)

  • Create a parameterized method that can be substituted for each repetitive method

  • Compile

  • Replace the body of one method with a call to the new method

  • Compile and Test

  • Use Inline Method on the old method

  • Repeat for all the methods.


  • Contributors


    • Bernd Kahlbrandt

    For more information see page 283 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Move Method


    A method is, or will be, using or used by more features of another class than the class on which it        is defined.

    Create a new method with a similar body in the class it uses most. Either turn the old method into a simple delegation, or remove it altogether.

      

    For more information see page 142 of Refactoring

    Additional Comments


    Marian Vittek sent an example  for moving a method to a method argument.

    class Project {
      Person[] participants;
    }

    class Person {
      int id;
      boolean participate(Project p) {
        for(int i=0; i           if (p.participants[i].id == id) return(true);
        }
        return(false);
      }  
    }

    ... if (x.participate(p)) ...


    After applying the move you end up with


    class Project {
      Person[] participants;
      boolean participate(Person x) {
        for(int i=0; i           if (participants[i].id == x.id) return(true);
        }
        return(false);
      }  
    }

    class Person {
      int id;
    }

    ... if (p.participate(x)) ...



    He also points out that the part of the Move Method mechanics that reads Determine how to reference the correct target object from the source, should be replaced by Determine how to reference the correct
    target object from the source or from arguments of the method
    which is more general.



    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Move Field


    A field is, or will be, used by another class more than the class on which it is defined.

    Create a new field in the target class, and change all its users.




    For more information see page 146 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Move Class


    Refactoring contributed by Gerard M. Davison

    You have a class that is in a package that contains other classes that it is not related to in function.

    Move the class to a more relevant package. Or create a new package if required for future use.


    class org.davison.ui.TextThing
    class org.davison.ui.TextProcessor
    class org.davison.log.Logger

    depends on

    class org.davison.ui.StringUtil


    class org.davison.ui.TextThing
    class org.davison.ui.TextProcessor
    class org.davison.log.Logger

    depends on

    class org.davison.util.StringUtil

    Motivation



    Classes are often created in a packages close to where they are being used, this can make sense until the class starts to be re-used by other parts of the product. The package in question might also have just become too big. (I have a preference that my packages never have more than about 10 classes)

    It is often better to move to this class to a package more related to it in form or function. This can help remove complex package level dependencies and make it easier for developers to find and re-use classes.

    If there are many dependencies for the class within its own package, then Extract Class could be used first to split out the relevant parts.

    Another example where is this used often is to move String resource objects into sub a res package to simplify localization compilation.

    Mechanics



    • Move the class to its new folder on the source tree.

    • Remove any class files generated from compiling this class at its old location.

    • Alter the package statement in the source file to reflect the new package.

    • Create import statements for any dependant classes in original package.

    • Compile and test the class in its new package, updating and moving unit tests as required using this same method.

    • Alter, and create in some cases, the import statements on any dependee class. Note this is easier if wide imports are not used.

    • Check for classes that might dynamically instantiate this class using java.lang.reflect or the ResourceBundle api. Also look for code that might reference this class as an absolute class literal.

    • Compile and test all code that was dependant on the original class.

    • Consider applying
      Extract Package
      when you have many classes of different functionality in a given package.



    Example



      Lets look at the header of StringUtil


      package org.davison.ui

      // imports

      public class StringUtil

      ...

      We move the file and change the package header.


      package org.davison.util

      // imports

      public class StringUtil

      ...

      I can now compile and unit test this class in its new package. If the unit tests are in a different package class file then they might need to be updated.

      I can now go on to update the dependant classes. For example here I update the import statements, I have not used a wide imports in this example but the principle is the same.


      package org.davison.log

      // imports

      import org.davison.ui.StringUtils;

      // more imports

      public class Logger

      ...


      This becomes:


      package org.davison.log

      // imports

      import org.davison.util.StringUtils;

      // more imports

      public class Logger

      ...



      Again compile and test this new class with the imports.

      As mentioned before there are other ways of dynamically loading classes. You may have to look for class literals and strings constants containing the fully qualified name of the old class.




      // A class literal

      public Class m_utils = org.davison.ui.StringUtils.class;

      // A dynamically loaded class

      public Class m_utils = Class.forName("org.davison.ui.StringUtils");

      // A loaded resource bundle

      public ResourceBundle m_bundle = ResourceBundle.getBundle(
         "org.davison.ui.StringUtils");




      These can be fixed using simple find and replaces, these dynamic examples will also be found using the units tests for all classes.





      // A class literal, this will be found by the compiler.

      public Class m_utils = org.davison.util.StringUtils.class;

      // A dynamically loaded class

      public Class m_utils = Class.forName("org.davison.util.StringUtils");

      // A loaded resource bundle

      public ResourceBundle m_bundle = ResourceBundle.getBundle(
         "org.davison.util.StringUtils");





      One all static and dynamic cases have been dealt with, and all unit tests have run. The refactoring is complete.


    Additional Comments


    Consider using a tool to do this. See Chris Sequin's refactory or Woodenchair

    --Martin Fowler




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Move Business Logic to Session


    Inter-entity bean relationships introduce overhead in the model

    Encapsulate the workflow related to inter-entity bean relationships in a session bean (Session Facade)


    For further information see page 112 of Core J2EE Patterns by Alur, Crupi, and Malks




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Merge Session Beans


    Create a one-to-one mapping between session beans and entity beans

    Map coarse-grained business services to session beans. Eliminate or combine session beans that act solely as entity bean proxies into session beans that represent coarse grained business services


    For further information see page 106 of Core J2EE Patterns by Alur, Crupi, and Malks




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Localize Disparate Logic


    Business logic and presentation formatting are intermingled within a JSP view

    Extract business logic into one or more helper classes that can be used by the JSP or by a controller


    For further information see page 83 of Core J2EE Patterns by Alur, Crupi, and Malks




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로
    잘 못 됨.
    다중에 다시..

    http://www.refactoring.com/catalog/introduceSynchronizerToken.html



    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Parameter Object


    You have a group of parameters that naturally go together.

    Replace them with an object.



    For more information see page 295 of Refactoring


    Additional Comments


    Dealing with a chain of calls


    Ralph Johnson pointed out to me that a common case isn't clear in the Refactoring book. This case is when you have a bunch of methods that call each other, all of which have a clump of parameters that need this refactoring. In this case you don't want to apply Introduce Parameter Object because it would lead to lots of new objects when you only want to have one object that's passed around.

    The approach to use is to start with the head of the call chain and apply Introduce Parameter Object there. Then apply

    Preserve Whole Object
    to the other methods.




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Null Object



    You have repeated checks for a null value.

    Replace the null value with a null object.


                            if (customer == null) plan = BillingPlan.basic();
                            else plan = customer.getPlan();



    For more information see page 260 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Local Extension



    A server class you are using needs several additional methods, but you can't modify the class.

    Create a new class that contains these extra methods. Make this extension class a subclass or a wrapper of the original.




    For more information see page 164 of Refactoring

    Additional Comments


    Using Free Functions in C++


    --Andy Glew writes:

    I comment on this in the light of the recent comp.lang.c++.moderated discussion of using object.method() or free functions,
    possibly friends, in a class interface. I.e. this comment is
    more C++ oriented than Java oriented.

    Briefly, if the original class used free functions for this sort
    of interface, the assymmetry introduced by wrapping a class's
    object.methods() would not exist.  In C++

        class MfDateSub {
            private: Date _original;
            // wrapping the symmetric operator after(date1,date2)
            public:  friend bool after (MfDateSub arg1, MfDateSub arg2) {
                return after(arg1._original,arg2._original);
            }
            public:  friend bool after (MfDateSub arg1, Date arg2) {
                return after(arg1._original,arg2);
            }
            public:  friend bool after (Date arg1, MfDateSub arg2) {
                return after(arg1,arg2._original);
            }
       };

    Now all combinations work; the wrapping is more invisible to the user.

    Even better if the free function after(date1,date2) does not require friendship --- although the definition of the extensions gets spread
    into a few more places.

    Note that in the example above, however, an implicit conversion of a MfDateSub value to a Date would suffice.

        class MfDateSub {
                operator Date() const { return _original }
        }

    although this approach cannot always work, e.g. when there
    is extra data to be compared in the extension.

    CONCLUSION: using free functions in an interface allows a greater
    degree of syntactic transparency when wrapping.

    Return type for extra methods


    --Dmitri Colebatch wrote in to ask why when using the subclass I didn't return the subclass in the extra methods. So I have added the nextDay method to MfDate, but it returns a Date not an MfDate. I can't think of any good reason to return the superclass, so I agree with him and suggest returning MfDate instead. The same is true for the wrapper implementation as well. After all if the client is using the extra method, they are aware of the new class. This way they avoid having to futz with the returned object if they want to invoke other extension methods.




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Foreign Method



    A server class you are using needs an additional method, but you can't modify the class.

    Create a method in the client class with an instance of the server class as its first argument.


    Date newStart = new Date (previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1);



    Date newStart = nextDay(previousEnd);

    private static Date nextDay(Date arg) {
            return new Date (arg.getYear(),arg.getMonth(), arg.getDate() + 1);
    }

    For more information see page 162 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Explaining Variable



    You have a complicated expression.

    Put the result of the expression, or parts of the expression, in a temporary variable with a name that explains the purpose.

            if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
                  (browser.toUpperCase().indexOf("IE") > -1) &&
                  wasInitialized() && resize > 0 )
            {
                  // do something
            }

            final boolean isMacOs     = platform.toUpperCase().indexOf("MAC") > -1;
            final boolean isIEBrowser = browser.toUpperCase().indexOf("IE")  > -1;
            final boolean wasResized  = resize > 0;

            if (isMacOs && isIEBrowser && wasInitialized() && wasResized)
            {
                    // do something
            }

    For more information see page 124 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Business Delegate


    Session beans in the business tier are exposed to clients in other tiers

    Use a business delegate to decouple the tiers and to hide the implementation details

    For further information see page 106 of Core J2EE Patterns by Alur, Crupi, and Malks



    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce Assertion


    A section of code assumes something about the state of the program.

    Make the assumption explicit with an assertion.

            double getExpenseLimit() {
                    // should have either expense limit or a primary project
                    return (_expenseLimit != NULL_EXPENSE) ?
                            _expenseLimit:
                            _primaryProject.getMemberExpenseLimit();
            }


            double getExpenseLimit() {
                    Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null);
                    return (_expenseLimit != NULL_EXPENSE) ?
                            _expenseLimit:
                            _primaryProject.getMemberExpenseLimit();
            }

    For more information see page 267 of Refactoring



    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Introduce A Controller


    Control logic is scattered throughout the application, typically duplicated in multiple Java Server Page (JSP) views

    Extract control logic into one or more controller classes that serve as the initial contact point for handling a client request


    For further information see page 74 of Core J2EE Patterns by Alur, Crupi, and Malks




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Inline Temp



    You have a temp that is assigned to once with a simple expression, and the temp is getting in the way of other refactorings.

    Replace all references to that temp with the expression.

                    double basePrice = anOrder.basePrice();
                    return (basePrice > 1000)

                    return (anOrder.basePrice() > 1000)

    For more information see page 119 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Inline Method



    A method's body is just as clear as its name.

    Put the method's body into the body of its callers and remove the method.


            int getRating() {
                    return (moreThanFiveLateDeliveries()) ? 2 : 1;
            }
            boolean moreThanFiveLateDeliveries() {
                    return _numberOfLateDeliveries > 5;
            }



            int getRating() {
                    return (_numberOfLateDeliveries > 5) ? 2 : 1;
            }


    For more information see page 117 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Inline Class



    A class isn't doing very much.

    Move all its features into another class and delete it.



    For more information see page 154 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Hide presentation tier-specific details from the business tier


    Request handling and/or protocol-related data structures are exposed from the presentation tier to the business tier

    Remove all references to request handling and protocol-related presentation tier data structures from the business tier. Pass values between tiers using more generic data structures.


    For further information see page 91 of Core J2EE Patterns by Alur, Crupi, and Malks




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Hide Method



    A method is not used by any other class.

    Make the method private.


    For more information see page 303 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Hide Delegate



    A client is calling a delegate class of an object.

    Create methods on the server to hide the delegate.


    For more information see page 157 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Form Template Method


    You have two methods in subclasses that perform similar steps in the same order, yet the steps are different.


    Get the steps into methods with the same signature, so that the original methods become the same. Then you can pull them up.

            
    For more information see page 345 of Refactoring




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Extract Superclass


    You have two classes with similar features.

    Create a superclass and move the common features to the superclass.



    For more information see page 336 of Refactoring



    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Extract Subclass


    A class has features that are used only in some instances.


    Create a subclass for that subset of features.




    For more information see page 330of Refactoring



    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Extract Package


    Refactoring contributed by  Gerard M. Davison


    A package either has too many classes to be easily understandable or it suffers from the 'Promiscuous packages' smell.


    Extract a sub package depending on their gross dependencies or usages.


    interface org.davison.data.DataProvider
    class org.davison.data.DataFactory
    // Database classes
    class org.davison.data.JDBCProvider
    class org.davison.data.JDBCHelper
    class org.davison.data.JDBCUtils




    interface org.davison.data.DataProvider
    class org.davison.data.DataFactory
    // Database classes
    class org.davison.data.jdbc.JDBCProvider
    class org.davison.data.jdbc.JDBCHelper
    class org.davison.data.jdbc.JDBCUtils

    Motivation


    Dependencies will eventually cause problem in projects of any size, so it make sense to start refactoring sooner rather than later in order to make it clear which part of the code uses what.

    This sort of change can make the code more flexible. For example if you are writing a UI tool and then decide a command line variant is required. Then unless the code is properly structured you will have trouble re-using certain components. Packaging is one way of making dependencies explicit.

    This refactoring can be useful when a package becomes too large to be easily understood. For example in a diagrammer framework you might like to extra sub packages for important groups such as 'shapes'; 'ui' and 'printing'. This makes it easier to identify the use of a class by its implied association in a package.

    The structure produced here is also one that is recommeneded for use with the Abstract Factory pattern. Indeed this is how the example I have provided is structured.

    Mechanics



    • Work out groupings for your classes. Where required use the
      Extract Superclass
        to pull together any generic code first.

    • Create the new package and perform  
      Move Class
      for each file that needs to be moved. It is often efficient to move groups of classes at once.

    • Compile the code in the parent package and retest. The code in the sperate package will have been tested as part of the
      Move Class
      refactoring.

    • The refactorings at this point can be considered complete.

    • You might like to convert the code in the factory to using dynamic class loading by using the
      Convert Static to Dynamic Construction
      . This would enable selective inclusion of sub-packages depending on the build environment.


    Example


      There are no code examples as most of the work in done in
      Move Class
      .

    Additional Comments


    The way I've done this is to move all the classes in one go. (I'm prepared to take the big step first here since all the errors can be found with compiling.)

    I ensure the original package is dependent on the extracted package but the extracted package is independent of the original. Once I've done the move I compile the original package. The errors here can be fixed with import statements in the offending classes. I fix these errors until the original package compiles

    Then I work on the extracted package. This may compile fine, the problem lies if you have a class in the extracted package referring to a class in the original package. I move it back if I can. If not I use
    Extract Interface
    to create an interface which captures the way the classes in the extracted pacakge refer to the original class. I then place the extracted interface in the extracted package.

    --Martin Fowler




    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    Extract Method



    You have a code fragment that can be grouped together.

    Turn the fragment into a method whose name explains the purpose of the method.

            void printOwing() {
                    printBanner();

                    //print details
                    System.out.println ("name:        " + _name);
                    System.out.println ("amount        " + getOutstanding());
            }




            void printOwing() {
                    printBanner();
                    printDetails(getOutstanding());
            }

            void printDetails (double outstanding) {
                    System.out.println ("name:        " + _name);
                    System.out.println ("amount        " + outstanding);
            }

    For more information see page 110 of Refactoring





    ***** 아름다운프로님에 의해서 게시물 복사 + 카테고리변경되었습니다 (2003-12-18 17:27)
    Posted by 아름프로

    BLOG main image

    카테고리

    분류 전체보기 (539)
    이야기방 (19)
    토론/정보/사설 (16)
    IBM Rational (9)
    U-IT (0)
    SOA/WS/ebXML (110)
    개발방법론/모델링 (122)
    J2SE (34)
    J2EE (60)
    DataBase (39)
    Open Projects (30)
    BP/표준화 (50)
    Apache Projects (15)
    Web/보안/OS (22)
    Tools (7)
    AJAX/WEB2.0 (1)
    Linux/Unix (1)
    영어 (0)
    비공개방 (0)

    최근에 올라온 글

    최근에 달린 댓글

    최근에 받은 트랙백

    달력

    «   2025/02   »
    1
    2 3 4 5 6 7 8
    9 10 11 12 13 14 15
    16 17 18 19 20 21 22
    23 24 25 26 27 28

    글 보관함

    Total :
    Today : Yesterday :