#  Copyright (c) 2007, Enthought, Inc.
#  License: BSD Style.

#--(Interfaces)-----------------------------------------------------------------
"""
Interfaces
==========

In Traits 3.0, the ability to define, implement and use *interfaces* has been
added to the package.

Defining Interfaces
-------------------

Interfaces are defined by subclassing from the **Interface** class, as shown
in the example below::

    from traits.api import Interface

    class IName ( Interface ):

        def get_name ( self ):
            " Returns the name of an object. "

This same code is shown in the **IName Interface** tab of the code.

Interface classes are intended mainly as documentation of the methods and
traits that the interface defines, and should not contain any actual
implementation code, although no check is performed to enforce this currently.

Implementing Interfaces
-----------------------

A class declares that it implements one or more interfaces using the
**implements** function, which has the form::

    implements( interface [, interface2, ..., interfacen] )

The semantics of this function is that the class declares that it implements
each of the *interfaces* specified as an argument to **implements**.

Also, the call to **implements** must occur at class scope within the class
definition, as shown in the following example::

    from traits.api import HasTraits, implements

    class Person ( HasTraits ):

        implements( IName )

        ...

Only a single call to **implements** should occur within a class definition.

Refer to the **Person Class** tab in the code for a complete example of using
**implements**.

Note that in the current version, traits does not check to ensure that the
class containing the **implements** function actually implements the interfaces
it says it does.

Using Interfaces
----------------

Being able to define and implement interfaces would be of little use without
the ability to *use* interfaces in your code. In traits, using an interface is
accomplished using the **Instance** trait, as shown in the following example::

    from traits.api import HasTraits, Instance

    class Apartment ( HasTraits ):

        renter = Instance( IName )

Using an interface class in an **Instance** trait definition declares that the
trait only accepts values which are objects that either:

- Implement the specified interface.
- Can be adapted to an object that implements the specified interface.

Additional information on what it means to *adapt* an object to implement an
interface is presented in the next section of the tutorial.

As before, the **Instance** trait can also be used with classes that are not
interfaces, such as::

    from traits.api import HasTraits, Instance

    class Apartment ( HasTraits ):

        renter = Instance( Person )

In this case, the value of the trait must be an object which is an instance of
the specified class or one of its subclasses.
"""
#--<Imports>--------------------------------------------------------------------

from traits.api import *

#--[IName Interface]------------------------------------------------------------

# Define the 'IName' interface:
class IName ( Interface ):

    def get_name ( self ):
        """ Returns the name of an object. """

#--[Person Class]---------------------------------------------------------------

class Person ( HasTraits ):

    implements( IName )

    first_name = Str( 'John' )
    last_name  = Str( 'Doe' )

    # Implementation of the 'IName' interface:
    def get_name ( self ):
        """ Returns the name of an object. """
        return ('%s %s' % ( self.first_name, self.last_name ))

#--[Apartment Class]------------------------------------------------------------

# Define a class using an object that implements the 'IName' interface:
class Apartment ( HasTraits ):

    renter = Instance( IName )

#--[Example*]--------------------------------------------------------------------

# Create an object implementing the 'IName' interface:
william = Person( first_name = 'William', last_name = 'Adams' )

# Create an apartment, and assign 'renter' an object implementing 'IName':
apt = Apartment( renter = william )

# Verify that the object works correctly:
print 'Renter is:', apt.renter.get_name()

