How can the proxy pattern be used to replace a singleton?
Asked Answered
F

3

13

This is in response to some comments in what is so bad about singletons

There it was suggested that the proxy pattern can be used instead of a singleton to cache DB data. But I cannot see the advantage, and in fact the singleton seems more "controllable".

Let me elaborate on the problem. Assume you have a database, with a large set of data, that never changes so it can be regarded read only, why would the proxy pattern be a better way of modelling this data cache than the singleton?

(PS: if you are going to say "because its more 'testable' !" - please elaborate, I am still getting used to those concepts)

Thanks for your help!

Fulmination answered 19/1, 2010 at 15:6 Comment(1)
design patterns mindmap: mindmeister.com/7008138/design-patternsClotildecloture
S
9

disclaimer: I'm speaking in java terms here

singleton are now considered an antipattern mostly because have been abused a lot lately as they are a quick and convenient way to share data across the application - which is somewhat an overextension of the design pattern which is more suited to provide acces control to a shared resource.

consider a program standard output: the access of that resource needs to be guarded by a single point of access to allow for synchronization of the writes, that is why you have for example System.out as a static instance in java.

the problem is, when you start having singleton you'll need to know every nitty gritty details of what you're doing, because you are making lot of strict assumption on your singleton class, the most important one that it will be the only one class in the system. then you start using it, assuming that it will always be a single entry point to your resource, and then nasty bug arises because your class has now been deployed on an ejb server and each ejb context has it's own singleton, plus one more singleton for every jsp that has been reloaded from the server, plus one singleton for every time that your singleton has been serialized and deserialized (as you probably have forgot to override readResolve() method).

so this is why singleton have to be used with lot of care, and are now considered an antipattern in spite of them being totally useful for their intended usage.

in the case of a database cache, it would be a better approach to have each class in need of the cache using a proxy for this "cache" resource, so you may add the logic to "find the resource" within the proxy itself instead of having the logic be tied to the retrieval of the cache singleton, which may or may not work depending on the environment.

so in few words using singleton as means to have a shared access to a resource is bad, because you are hardcoding the method of finding the resource (and ignoring the singleton pitfalls) while having singleton to control a resource for synchronization purpose is totally acceptable.

think of semaphores, those works only if you can get the same semaphore always. in this latter case what may be a problem is accessing the singleton from everywhere you need to access that semaphore: here you'll need some class to wrap the singleton up and provide a finer control of the lifecycle of the semaphore itself.

proxy are intended to cover the role of "providing a resource across the system", be it a single application, a client server system, different components of the same system and so on, with the added benefit that with èrpxy the method of retrieval of the resource is opaque. you may have them providing you a singleton containing an hashmap of the cached values, you may have them accessing a memcached somwhere on the network, you may have them reading a csv during tests, all without changing how you call them from the application.

Soapstone answered 1/2, 2010 at 22:31 Comment(2)
Hi, some good points. The problem I have is I do not want the DB config to be parsed every time someone wants to access this cached data, as this is slow. Populating the cache is also a slow-ish process. Previously every request took about 7seconds, now its less than 1. (Now only the first is slow, from then on, you don't notice). I like the point that the proxy pattern need not care where cache is located, and reducing the dependancy between the application.Fulmination
of course a cache is a particular kind of resource which benefits from both system wide availability and opaque access. for this kind of problem a third pattern may be used: the named service stored in a directory. you can see this pattern a lot in the ejb and javaEE in general, using the InitialContext class, but you can have a lot of different kind of contexts. long story short: you have your application creating the cache as singleton at startup, register it inside a Context under a known name (imagine a rmi bind) and then you may create a proxy to access this context to retrieve the cacheSoapstone
C
2

In my opinion, there is no "either, or". A class can implement several design patterns at the same time. I would say the class implementing the access to the external database is in any case a Proxy (a remote proxy in this case). If you consider the caching an extra functionality it is also a Decorator.

So, the real question is, should it also be a Singleton? Let us assume there is exactly one external database. Does there need to be only one CachingDBProxy? I would say, it depends on the use:

If there are several clients accessing similar data, they can clearly benefit if they share the same CachingDBProxy. The data needed by one client may already have been needed by another client, so it can be retrieved from the cache instead of having to perform a costly access to the database.

On the other hand, some clients may access quite distinct pieces of data. So, if we assume that the CachingDBProxy only caches a limited amount of data access by one group of clients may throw out data still needed by another group of clients, causing degradation of cache performance. In this case it may be reasonable to have multiple CachingDBProxies even though there is only one database (this assumes of course, that concurrent access is possible).

So, how many CachingDBProxies there should be, depends on the use. The CachingDBProxy should not limit its use without good reason, so it should not enforce that there is only one instance, So, in this case the CachingDBProxies should be no Singleton, IMHO. Only the clients can know how many CachingDBProxies are good for them.

On the other hand, if we have a Proxy for specific resource that can only handle one access at a time, it may have to be a Singleton. But that is a distinct case from the above case. Here the requirement comes directly from the area the Proxy is responsible for (its purpose is to channel the access to that specific resource).

Calamanco answered 30/1, 2010 at 15:47 Comment(1)
Yes, I see your point. I guess if you analyse the design of most classes, they do turn out to implement a number of patterns, all mashed up into one.Fulmination
D
0

I can only imagine that the Proxy Pattern is used to Proxy between the cached Data and loading the Data (aka lazy loading).

Sort of:

class DbProxy
{
  private static Data cache = null; // Sort-of Singleton

  public static Data GetData(String query)
  {
    if (DbProxy.cache == null)
    {
      // Data = Do Stuff to read Data
      DbProxy.cache = Data;
    }
    return DbProxy.cache;
  }
}

This has the Advantage that the Code using this does not have to care about wether the Data already exists, it just has to call GetData and done.

/* Disclaimer: Code is not valid, it's just pseudo just for demonstration purposes */

Drusie answered 19/1, 2010 at 15:17 Comment(2)
Yes, but to me this looks just like a singleton. Maybe my singleton is already a proxy?Fulmination
I am no expert in design patterns and i have no CS degree or anything, so i might be wrong, but to my knowledge those patterns are quite related. The Proxy pattern just might do more work (i.e. check if the result is cacheable, if not always repeat the query instead of returning the cached item).Drusie

© 2022 - 2024 — McMap. All rights reserved.