⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pat3e-1.htm

📁 四人帮《设计模式》一书英文版本
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<A NAME="auto1041"></A>
<PRE>
    new
        self error: 'cannot create new object'
    
    default
        SoleInstance isNil ifTrue: [SoleInstance := super new].
        ^ SoleInstance
</PRE>

</LI>

<A NAME="single-subclass"></A>
<LI><EM>Subclassing the Singleton class.</EM>
The main issue is not so much defining the subclass but installing its
unique instance so that clients will be able to use it.  In essence,
the variable that refers to the singleton instance must get
initialized with an instance of the subclass.  The simplest technique
is to determine which singleton you want to use in the Singleton's
<CODE>Instance</CODE> operation.  An example in the Sample Code shows how
to implement this technique with environment variables.

<A NAME="auto1042"></A>
<P>Another way to choose the subclass of Singleton is to take the
implementation of <CODE>Instance</CODE> out of the parent class (e.g.,
<A HREF="pat3afs-1.htm#MazeFactory-def" tppabs="http://ultra/development/DesignPatterns/lowres/pat3afs.htm#MazeFactory-def" TARGET="_mainDisplayFrame"><CODE>MazeFactory</CODE></A>) and put it in the subclass.  That lets a C++
programmer decide the class of singleton at link-time (e.g., by
linking in an object file containing a different implementation) but
keeps it hidden from the clients of the singleton.</P>

<A NAME="auto1043"></A>
<P>The link approach fixes the choice of singleton class at link-time,
which makes it hard to choose the singleton class at run-time. Using
conditional statements to determine the subclass is more flexible, but
it hard-wires the set of possible Singleton classes. Neither approach
is flexible enough in all cases.</P>

<A NAME="single-registry"></A>
<P>A more flexible approach uses a <STRONG>registry of singletons</STRONG>.
Instead of having <CODE>Instance</CODE> define the set of possible
Singleton classes, the Singleton classes can register their singleton
instance by name in a well-known registry.</P>

<A NAME="auto1044"></A>
<P>The registry maps between string names and singletons. When
<CODE>Instance</CODE> needs a singleton, it consults the registry, asking
for the singleton by name. The registry looks up the corresponding
singleton (if it exists) and returns it. This approach frees
<CODE>Instance</CODE> from knowing all possible Singleton classes or
instances. All it requires is a common interface for all Singleton
classes that includes operations for the registry:</P>

<A NAME="auto1045"></A>
<PRE>
    class Singleton {
    public:
        static void Register(const char* name, Singleton*);
        static Singleton* Instance();
    protected:
        static Singleton* Lookup(const char* name);
    private:
        static Singleton* _instance;
        static List&lt;NameSingletonPair>* _registry;
    };
</PRE>

<A NAME="auto1046"></A>
<P><CODE>Register</CODE> registers the Singleton instance under the given
name. To keep the registry simple, we'll have it store a list of
<CODE>NameSingletonPair</CODE> objects. Each <CODE>NameSingletonPair</CODE>
maps a name to a singleton. The <CODE>Lookup</CODE> operation finds a
singleton given its name. We'll assume that an environment variable
specifies the name of the singleton desired.</P>

<A NAME="auto1047"></A>
<PRE>
    Singleton* Singleton::Instance () {
        if (_instance == 0) {
            const char* singletonName = getenv("SINGLETON");
            // user or environment supplies this at startup
    
            _instance = Lookup(singletonName);
            // Lookup returns 0 if there's no such singleton
        }
        return _instance;
    }
</PRE>

<A NAME="auto1048"></A>
<P>Where do Singleton classes register themselves? One possibility is in
their constructor. For example, a <CODE>MySingleton</CODE> subclass could
do the following:</P>

<A NAME="auto1049"></A>
<PRE>
    MySingleton::MySingleton() {
        // ...
        Singleton::Register("MySingleton", this);
    }
</PRE>

<A NAME="single-cpp-imp2"></A>
<P>Of course, the constructor won't get called unless someone
instantiates the class, which echoes the problem the Singleton pattern
is trying to solve!  We can get around this problem in C++ by defining
a static instance of <CODE>MySingleton</CODE>.  For example, we can
define</P>

<A NAME="auto1050"></A>
<PRE>
    static MySingleton theSingleton;
</PRE>

<A NAME="auto1051"></A>
<P>in the file that contains <CODE>MySingleton</CODE>'s implementation.</P>

<A NAME="auto1052"></A>
<P>No longer is the Singleton class responsible for creating the
singleton.  Instead, its primary responsibility is to make the
singleton object of choice accessible in the system. The static object
approach still has a potential drawback&#151;namely that instances of all
possible Singleton subclasses must be created, or else they won't get
registered.</P>

</OL>

<A NAME="samplecode"><A>
<H2><A HREF="#knownuses"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next: Known Uses"></A> Sample Code</H2> 

<A NAME="auto1053"></A>
<P>Suppose we define a <CODE>MazeFactory</CODE> class for building
mazes as described on <A HREF="pat3afs-1.htm#MazeFactory-def" tppabs="http://ultra/development/DesignPatterns/lowres/pat3afs.htm#MazeFactory-def" TARGET="_mainDisplayFrame">page 92</A>.
<CODE>MazeFactory</CODE> defines an interface for building different
parts of a maze.  Subclasses can redefine the operations to return
instances of specialized product classes, like <CODE>BombedWall</CODE>
objects instead of plain <CODE>Wall</CODE> objects.</P>

<A NAME="mazebld-single"></A>
<P>What's relevant here is that the Maze application needs only one
instance of a maze factory, and that instance should be available to
code that builds any part of the maze.  This is where the Singleton
pattern comes in.  By making the <CODE>MazeFactory</CODE> a singleton, we
make the maze object globally accessible without resorting to global
variables.</P>

<A NAME="auto1054"></A>
<P>For simplicity, let's assume we'll never subclass
<CODE>MazeFactory</CODE>.  (We'll consider the alternative in a moment.)
We make it a Singleton class in C++ by adding a static
<CODE>Instance</CODE> operation and a static <CODE>_instance</CODE> member
to hold the one and only instance.  We must also protect the
constructor to prevent accidental instantiation, which might lead to
more than one instance.</P>

<A NAME="auto1055"></A>
<PRE>
    class MazeFactory {
    public:
        static MazeFactory* Instance();
    
        // existing interface goes here
    protected:
        MazeFactory();
    private:
        static MazeFactory* _instance;
    };
</PRE>

<A NAME="auto1056"></A>
<P>The corresponding implementation is</P>

<A NAME="auto1057"></A>
<PRE>
    MazeFactory* MazeFactory::_instance = 0;
    
    MazeFactory* MazeFactory::Instance () {
        if (_instance == 0) {
            _instance = new MazeFactory;
        }
        return _instance;
    }
</PRE>

<A NAME="auto1058"></A>
<P>Now let's consider what happens when there are subclasses of
<CODE>MazeFactory</CODE>, and the application must decide which one to
use.  We'll select the kind of maze through an environment variable
and add code that instantiates the proper <CODE>MazeFactory</CODE>
subclass based on the environment variable's value.  The
<CODE>Instance</CODE> operation is a good place to put this code, because it
already instantiates <CODE>MazeFactory</CODE>:</P>

<A NAME="auto1059"></A>
<PRE>
    MazeFactory* MazeFactory::Instance () {
        if (_instance == 0) {
            const char* mazeStyle = getenv("MAZESTYLE");
    
            if (strcmp(mazeStyle, "bombed") == 0) {
                _instance = new BombedMazeFactory;
    
            } else if (strcmp(mazeStyle, "enchanted") == 0) {
                _instance = new EnchantedMazeFactory;
    
            // ... other possible subclasses
    
            } else {        // default
                _instance = new MazeFactory;
            }
        }
        return _instance;
    }
</PRE>

<A NAME="auto1060"></A>
<P>Note that <CODE>Instance</CODE> must be modified whenever you define a
new subclass of <CODE>MazeFactory</CODE>.  That might not be a problem in
this application, but it might be for abstract factories defined in a
framework.</P>

<A NAME="auto1061"></A>
<P>A possible solution would be to use the registry approach described in
the Implementation section.  Dynamic linking could be useful here as
well&#151;it would keep the application from having to load all the
subclasses that are not used.</P>

<A NAME="knownuses"><A>
<H2><A HREF="#relatedpatterns"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next: Related Patterns"></A> Known Uses</H2> 

<A NAME="smalltalk-use-single"></A>
<P>An example of the Singleton pattern in
Smalltalk-80 [<A HREF="bibfs-1.htm#parcplace_smalltalk" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#parcplace_smalltalk" TARGET="_mainDisplayFrame">Par90</A>] is the set of changes to the
code, which is <CODE>ChangeSet current</CODE>.  A more subtle example is
the relationship between classes and their <a href="chapAfs-1.htm#metaclass" tppabs="http://ultra/development/DesignPatterns/lowres/chapAfs.htm#metaclass" target="_mainDisplayFrame">metaclasses</a>.  A
metaclass is the class of a class, and each metaclass has one
instance.  Metaclasses do not have names (except indirectly through
their sole instance), but they keep track of their sole instance and
will not normally create another.</P>

<A NAME="auto1062"></A>
<P>The InterViews user interface toolkit [<A HREF="bibfs-1.htm#InterViews3.1" tppabs="http://ultra/development/DesignPatterns/lowres/bibfs.htm#InterViews3.1" TARGET="_mainDisplayFrame">LCI+92</A>] uses the
Singleton pattern to access the unique instance of its Session and
WidgetKit classes, among others.  Session defines the application's
main event dispatch loop, stores the user's database of stylistic
preferences, and manages connections to one or more physical displays.
WidgetKit is an <A HREF="pat3afs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A> for
defining the look and feel of user interface widgets.  The
<CODE>WidgetKit::instance()</CODE> operation determines the particular
WidgetKit subclass that's instantiated based on an environment
variable that Session defines.  A similar operation on Session
determines whether monochrome or color displays are supported and
configures the singleton Session instance accordingly.</P>

<A NAME="relatedpatterns"></A>
<H2><A HREF="#last"><IMG SRC="down3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/down3.gif" BORDER=0 ALT="next: navigation"></A> Related Patterns</H2> 

<A NAME="auto1063"></A>
<P>Many patterns can be implemented using the Singleton pattern.
See <A HREF="pat3afs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3afs.htm" TARGET="_mainDisplayFrame">Abstract Factory (87)</A>,
<A HREF="pat3bfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3bfs.htm" TARGET="_mainDisplayFrame">Builder (97)</A>, and <A
HREF="pat3dfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3dfs.htm" TARGET="_mainDisplayFrame">Prototype (117)</A>.</P>

<A NAME="last"></A>
<P><A HREF="#intent"><IMG SRC="up3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/up3.gif" BORDER=0></A><BR>
<A HREF="disc3fs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/disc3fs.htm" TARGET="_mainDisplayFrame"><IMG SRC="rightar3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/rightar3.gif"
	ALIGN=TOP BORDER=0></A> <A HREF="disc3fs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/disc3fs.htm"
	TARGET="_mainDisplayFrame">Discussion of Creational Patterns</A><BR>
<A HREF="pat3dfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3dfs.htm" TARGET="_mainDisplayFrame"><IMG SRC="leftarr3-1.gif" tppabs="http://ultra/development/DesignPatterns/lowres/gifsb/leftarr3.gif"
	ALIGN=TOP BORDER=0></A> <A HREF="pat3dfs-1.htm" tppabs="http://ultra/development/DesignPatterns/lowres/pat3dfs.htm"
	TARGET="_mainDisplayFrame">Prototype</A>
</P>

</BODY>

</HTML>


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -