Loading ...
Sorry, an error occurred while loading the content.

Monostates vs. Singletons (Was: Re: [XP] Singletons == Global s?)

Expand Messages
  • azami@speakeasy.net
    ... blame ... Better, but still not very good. In my opinion. Maybe I d like it better if some of the names were changed - getInstance() by convention
    Message 1 of 27 , Apr 2, 2001
    • 0 Attachment
      --- In extremeprogramming@y..., "Dinwiddie, George"
      <George.Dinwiddie@a...> wrote:
      > > From: Ryan King [mailto:rking@c...]
      > > I would at least think of it, because I the following is
      redundant:
      > >
      > > > String logPrefix = "foo: ";
      > > > log(logPrefix, [...]
      > > > log(logPrefix, [...]
      > > > log(logPrefix, [...]
      >
      > OK, putting the log prefix each time isn't a great solution. (I'll
      blame
      > it on my invisible pair.)
      >
      > How about this. Let's say I start with a singleton class like this:
      > ...
      > Now, we want to add prefixes:
      >
      > public class Logger {
      > private static final String emptyString = "";
      > private String prefix;
      > private static Map loggers = new HashMap();
      > private Logger(String prefix) {
      > loggers.put(prefix, this);
      > }
      > public void log(String message) {
      > System.out.println(prefix+message);
      > }
      > public static Logger getInstance() {
      > return getInstance(emptyString);
      > }
      > public static Logger getInstance(String prefix) {
      > Logger instance;
      > if (loggers.containsKey(prefix)) {
      > instance = (Logger)loggers.get(prefix);
      > } else {
      > instance = new Logger(prefix);
      > }
      > return instance;
      > }
      > }
      >
      > Does this smell better?

      Better, but still not very good. In my opinion. Maybe I'd like it
      better if some of the names were changed - getInstance() by convention
      implies singleton. This is only by convention, it doesn't enforce it
      in code or anything, and I'd always write code not to care whether the
      result of getInstance() is a singleton or not. Also, you'll end up
      with (potentially) different log object instances all trying to
      operate on the same resource, which could get tricky in a
      multi-threaded application. Not unsolvable, just unnecessary. :-)

      My idea: Write the logger as a singleton. Write an access class for
      the logger (could use the same interface) that is not a singleton and
      has state (the prefix).

      class PrefixedLogger {
      private String prefix;
      public String getPrefix() { return prefix; }
      public setPrefix(String prefix) { this.prefix = prefix; }

      public log(String message) {
      Logger.Log(prefix + ": " + message); //See NOTE below
      }
      }

      NOTE: I prefer in some cases to avoid the indirection of
      getInstance(), and use static methods instead. It's more work if/when
      you need more than one instance, but in the meantime the syntax is
      simple and intuitive - just like System.out. If you want proof that
      this is simpler, just watch how typical beginning Java programmers
      react to all the getInstance() and toolbox stuff!

      Reactions?

      -Matthew
      azami@...
    • Eric Hodges
      ... PrefixedLogger is fundamentally different from Logger, though. Logger is a Singleton. PrefixedLogger isn t. (I suggested this solution a week or 2 ago,
      Message 2 of 27 , Apr 2, 2001
      • 0 Attachment
        > -----Original Message-----
        > From: Dinwiddie, George [mailto:George.Dinwiddie@...]
        > Sent: Monday, April 02, 2001 2:40 PM
        > To: 'extremeprogramming@yahoogroups.com'
        > Subject: RE: Monostates vs. Singletons (Was: Re: [XP] Singletons ==
        > Global s?)
        >
        >
        > Good point. I thought of that possibility, but then thought that
        > I'd be more likely to collapse them into a single class, as shown,
        > at least until I needed to differentiate them. To me, it was an
        > easier refactoring.
        >
        > I notice that your PrefixedLogger is quite different from your
        > Logger.
        > - It doesn't use a factory to create it.
        > - It's not a derived class, so there's more work to convert
        > calling code from Logger to PrefixedLogger.

        PrefixedLogger is fundamentally different from Logger, though. Logger is a
        Singleton. PrefixedLogger isn't.

        (I suggested this solution a week or 2 ago, btw.)
      • Eric Bennett
        ... Oh, agreed. I would start with this about 10 seconds before I made System.out a variable so a new stream could be swapped in for testing. ... Less in my
        Message 3 of 27 , Apr 2, 2001
        • 0 Attachment
          On Mon, 2 Apr 2001, Dinwiddie, George wrote:

          > As for your suggestion, if you're going to go that
          > route, why not just condense it to
          >
          > public class Logger {
          > public static void Log(String message) {
          > System.out.println(message);
          > }
          > }
          >
          > In fact, the above is frequently my starting point in a program.

          Oh, agreed. I would start with this about 10 seconds before I made
          "System.out" a variable so a new stream could be swapped in for testing.


          > Does a static method have more or less smell than a singleton
          > accessed via a static factory method?

          Less in my book unless something non-global characterizes the factory.
          Maybe I just don't get it yet.


          - Eric B.

          --
          "LogFlume.add( new Log().add_rider( (FoolishLogger) this ) )"
        • Dinwiddie, George
          From: azami@speakeasy.net [mailto:azami@speakeasy.net] ... Well, my experience with the Java run-time-library doesn t suggest that getInstance() implies
          Message 4 of 27 , Apr 2, 2001
          • 0 Attachment
            From: azami@... [mailto:azami@...]
            > [snip]Maybe I'd like it
            > better if some of the names were changed - getInstance() by convention
            > implies singleton. This is only by convention, it doesn't enforce it
            > in code or anything, and I'd always write code not to care whether the
            > result of getInstance() is a singleton or not.

            Well, my experience with the Java run-time-library doesn't suggest that
            getInstance() implies singleton. Check out the various flavors of
            java.util.Calendar.getInstance, for example. Maybe I'm wrong, but I've
            standardized my code to use getInstance() for almost any factory
            method. When I first coded singletons in Java, I used getSingleton(),
            but quickly decided that my client code didn't care whether or not it
            was a singleton. (I think this was about when I first converted a
            singleton to a non-singleton.)

            > Also, you'll end up
            > with (potentially) different log object instances all trying to
            > operate on the same resource, which could get tricky in a
            > multi-threaded application. Not unsolvable, just unnecessary. :-)

            Not so tricky. It's easy to put synchronization inside the Logger
            class to make it thread safe.

            > My idea: Write the logger as a singleton. Write an access class for
            > the logger (could use the same interface) that is not a singleton and
            > has state (the prefix).
            [code snipped]
            > NOTE: I prefer in some cases to avoid the indirection of
            > getInstance(), and use static methods instead. It's more work if/when
            > you need more than one instance, but in the meantime the syntax is
            > simple and intuitive - just like System.out. If you want proof that
            > this is simpler, just watch how typical beginning Java programmers
            > react to all the getInstance() and toolbox stuff!

            Yes, using a static method works. For some reason I tend not to use
            that much any more. Somehow that seems more like a global, to me.

            In any event, I wouldn't judge which is simpler by the reaction of a
            novice programmer. I've seen too many that think sprinkling magic
            numbers around the program is simpler.

            So, among the options are:
            1. using a static method, which implies a global, singleton service,
            if not object.
            2. using a constructor (which perhaps just initializes a Monostate),
            which implies client control over the creation of objects.
            3. using a factory method, which implies to me that the client has
            no knowledge over whether a new object is being created or one is
            being pulled out of a pool (that may only have one element). To others,
            such as yourself, this seems to imply a singleton.

            Except for the amount of typing, I currently think that #3 is just as
            simple and is more general than #1 or #2. I don't use #3 exclusively,
            however, and I'm willing to consider arguments for the alternatives.

            - George
          • Dinwiddie, George
            From: Eric Hodges [mailto:eric.hodges@mongoosetech.com] ... a ... Does the client care whether it s a singleton, or not? To me, it s not that the client has a
            Message 5 of 27 , Apr 2, 2001
            • 0 Attachment
              From: Eric Hodges [mailto:eric.hodges@...]
              > PrefixedLogger is fundamentally different from Logger, though. Logger is
              a
              > Singleton. PrefixedLogger isn't.

              Does the client care whether it's a singleton, or not? To me, it's not that
              the client has a desire to do particular types of logging; it's that the
              client has a responsibility to the application to log significant
              occurrences.

              In fact, I don't think I'd ever want the clients of an class to know
              whether or not it's a singleton. It's not their business. If they
              start making assumptions about that, you might as well use a global.
              At least, that's my thinking.

              So, I don't think that singleton-or-not makes a fundamental difference. I
              think that's an implementation issue that should live inside the class. In
              fact, if we're going to start having different flavors of Logger, I think
              I'd be tempted to refactor Logger into an interface. I might also remove
              the factory method from any class and create a LoggerFactory, instead.

              In any event, I think I'm going to end up with a singleton root logging
              object, because I want a single point of control for the logging output.
              I'll also generally want to merger multiple log streams into one. Perhaps
              I'll want to have multiple log streams, but I wouldn't want that to be
              under the control of the client code that sends the log messages.

              - George
            • Eric Hodges
              ... PrefixedLogger *should* be quite different from Logger. PrefixedLogger isn t a Singleton, so it shouldn t be a subclass of a Singleton. PrefixedLogger
              Message 6 of 27 , Apr 2, 2001
              • 0 Attachment
                > -----Original Message-----
                > From: Dinwiddie, George [mailto:George.Dinwiddie@...]
                > Sent: Monday, April 02, 2001 3:36 PM
                > To: 'extremeprogramming@yahoogroups.com'
                > Subject: RE: Monostates vs. Singletons (Was: Re: [XP] Singletons ==
                > Global s?)
                >
                >
                > From: Eric Hodges [mailto:eric.hodges@...]
                > > PrefixedLogger is fundamentally different from Logger, though.
                > Logger is
                > a
                > > Singleton. PrefixedLogger isn't.
                >
                > Does the client care whether it's a singleton, or not? To me,
                > it's not that
                > the client has a desire to do particular types of logging; it's that the
                > client has a responsibility to the application to log significant
                > occurrences.

                The client doesn't care. I was responding to this:

                > I notice that your PrefixedLogger is quite different from your
                > Logger.
                > - It doesn't use a factory to create it.
                > - It's not a derived class, so there's more work to convert
                > calling code from Logger to PrefixedLogger.

                PrefixedLogger *should* be quite different from Logger. PrefixedLogger
                isn't a Singleton, so it shouldn't be a subclass of a Singleton.
                PrefixedLogger doesn't need a factory method, it can use "new".

                >
                > In fact, I don't think I'd ever want the clients of an class to know
                > whether or not it's a singleton. It's not their business. If they
                > start making assumptions about that, you might as well use a global.
                > At least, that's my thinking.
                >
                > So, I don't think that singleton-or-not makes a fundamental difference. I
                > think that's an implementation issue that should live inside the
                > class. In
                > fact, if we're going to start having different flavors of Logger, I think
                > I'd be tempted to refactor Logger into an interface. I might also remove
                > the factory method from any class and create a LoggerFactory, instead.

                You shouldn't try to make a non-Singleton subclass of a Singleton. If that
                means the clients treat Logger differently from PrefixedLogger, so be it.
              • azami@speakeasy.net
                ... convention ... it ... the ... that ... I ve ... My brain wasn t fully in gear before I engaged my fingers. You ve expressed my true thoughts far better
                Message 7 of 27 , Apr 2, 2001
                • 0 Attachment
                  --- In extremeprogramming@y..., "Dinwiddie, George"
                  <George.Dinwiddie@a...> wrote:
                  >
                  > From: azami@s... [mailto:azami@s...]
                  > > [snip]Maybe I'd like it
                  > > better if some of the names were changed - getInstance() by
                  convention
                  > > implies singleton. This is only by convention, it doesn't enforce
                  it
                  > > in code or anything, and I'd always write code not to care whether
                  the
                  > > result of getInstance() is a singleton or not.
                  >
                  > Well, my experience with the Java run-time-library doesn't suggest
                  that
                  > getInstance() implies singleton. Check out the various flavors of
                  > java.util.Calendar.getInstance, for example. Maybe I'm wrong, but
                  I've
                  > standardized my code to use getInstance() for almost any factory

                  My brain wasn't fully in gear before I engaged my fingers. You've
                  expressed my true thoughts far better than I did. In fact,
                  getInstance() implies factory to me, and factory implies more than one
                  instance. After all, if there's only one of a thing what the heck do
                  you need a factory for? Just use the thing.

                  > method. When I first coded singletons in Java, I used
                  getSingleton(),
                  > but quickly decided that my client code didn't care whether or not
                  it
                  > was a singleton. (I think this was about when I first converted a
                  > singleton to a non-singleton.)

                  Ideally, the client code should not know whether the object is a
                  singleton. You could argue that Logger.Log() looks more like a
                  singleton than Logger.getInstance().Log(), but

                  1 - you _can_ introduce instances without changing that interface if
                  you need to
                  2 - I think the result is simpler, and until you need more complexity
                  it's worth it.

                  > > Also, you'll end up
                  > > with (potentially) different log object instances all trying to
                  > > operate on the same resource, which could get tricky in a
                  > > multi-threaded application. Not unsolvable, just unnecessary. :-)
                  >
                  > Not so tricky. It's easy to put synchronization inside the Logger
                  > class to make it thread safe.

                  You need class-level synchronization. The most intuitive ways of
                  synchronizing synchronize on instances. So, two instances could try
                  to simultaneously access the same resource. You have to remember to
                  protect the resource, not the Logger objects. But since the Logger is
                  conceptually representative of the resource, this will be easily
                  missed...

                  > Yes, using a static method works. For some reason I tend not to use
                  > that much any more. Somehow that seems more like a global, to me.

                  I think the concept of a "global service" is good for services that
                  are global. It is vitally important that (from the client's
                  perspective) the service is stateless. One object's use of a global
                  service must not impact any other object's use of the service.

                  For this reason I think it's important to conceptually separate
                  methods for using the service from methods for configuring the
                  service.

                  E.g., it's safe to use System.out wherever you want, but using
                  System.SetOut() is rather heinous if not carefully controlled.

                  > In any event, I wouldn't judge which is simpler by the reaction of a
                  > novice programmer. I've seen too many that think sprinkling magic
                  > numbers around the program is simpler.

                  A Java beginner isn't necessarily a novice programmer. Anytime a
                  programmer says in frustration, "Why won't it just let me X?!" or "I
                  can't believe I need to do X, Y, and Z just to accomplish Q!", there's
                  a big red flag about complexity. People experienced in a language are
                  used to its unnecessary complexities and tend to overlook them easily.

                  > So, among the options are:
                  > 1. using a static method, which implies a global, singleton
                  service,
                  > if not object.

                  I like this until something more is needed. As I mentioned, a global
                  service does not strike me as a bad thing when you're providing a
                  service that is global.

                  > 2. using a constructor (which perhaps just initializes a
                  Monostate),
                  > which implies client control over the creation of objects.

                  This seems like a clever trick to avoid having to change a lot of
                  client code during a refactoring when you actually do need different
                  instances all over the place. IMHO, YAGNI.

                  > 3. using a factory method, which implies to me that the client has
                  > no knowledge over whether a new object is being created or one is
                  > being pulled out of a pool (that may only have one element). To
                  others,
                  > such as yourself, this seems to imply a singleton.

                  As I mentioned, kind of a brain fart. Factory implies things are
                  being made. What factory really says to me is that I'm giving up
                  control of construction so that the factory can create a class I don't
                  know about (or shouldn't care about or don't know enough to choose) -
                  like a subclass of the class I asked for, or an implementer of the
                  interface I asked for. Factories have to do with abstraction, not
                  singleton-ness.

                  > Except for the amount of typing, I currently think that #3 is just
                  as
                  > simple and is more general than #1 or #2. I don't use #3
                  exclusively,
                  > however, and I'm willing to consider arguments for the alternatives.

                  I prefer #1 for global services, and #3 for hiding a variety of
                  implementations/subclasse so that client code doesn't need to know
                  when new implementations/subclasses are added. These are very
                  different uses. I would only ever use #2 if the client code
                  conceptually wanted control over object creation and instances. I
                  can't really think of a case where the client wants to believe it has
                  its own instance but there's no actual need for different instances,
                  except maybe as a step during a refactoring between a singleton and
                  multipletons (in either direction).

                  Best,
                  Matthew
                  azami@...
                Your message has been successfully submitted and would be delivered to recipients shortly.