4
\$\begingroup\$

I have a class that I'm trying to determine a good name for. I don't know if this concept comes up a lot in programming or not, but I'm wondering if I can get some suggestions for a good name for the class.

Basically, this is how the class functions:

  1. The first time its Get() method is called, it returns value A.
  2. Every subsequent time, it returns value B.
  3. If enough time has passed (provided), return value A one time, then revert back to sending 'B'. Repeat ad finitum.

Is there any name for something that functions like this? It's somewhat like memoization, and kind of like caching, but not quite.

The application of this construct is so described: Every so often I want a method to load complex data from Active Directory to load into our database before returning the results from the table. This ensures that we have recent data in our database. However, the time to execute this is high, and 99% of the time nothing has changed. So I want to limit the checking for changes to long timespans in my web app. So, using this construct I can say "only do the long version every X hours, and once it's been done, don't do it again for X more hours."

Implementation

The ICurrentDateTimeProvider simply has a Now property so it can be faked for unit tests.

public interface ICurrentDateTimeProvider
{
    DateTime Now { get; }
}

public class CurrentDateTimeProvider : ICurrentDateTimeProvider
{
    public DateTime Now { get { return DateTime.Now; } }
}

public class ReturnDifferentValueEverySoOften<T>
{
    public ReturnDifferentValueEverySoOften(T firstAndSometimes, T most, TimeSpan flipTime = default(TimeSpan), ICurrentDateTimeProvider provider = null)
    {
        FirstAndSometimes = firstAndSometimes;
        MostOfTheTime = most;
        if (flipTime == default(TimeSpan))
            flipTime = TimeSpan.FromHours(6);
        FlipTime = flipTime;
        if (provider == null)
            provider = new CurrentDateTimeProvider();
        DateTimeProvider = provider;
    }

    public void SetTimeProvider(ICurrentDateTimeProvider provider)
    {
        DateTimeProvider = provider;
    }

    public T FirstAndSometimes;
    public T MostOfTheTime;

    ICurrentDateTimeProvider DateTimeProvider { get; set; }
    TimeSpan FlipTime;
    DateTime LastFlip { get; set; }
    DateTime NextFlip { get { return LastFlip.Add(FlipTime); } }
    bool ever = false;

    public T Get()
    {
        var now = DateTimeProvider.Now;
        if (!ever)
        {
            ever = true;
            LastFlip = now;
            return FirstAndSometimes;
        }
        if (NextFlip < now)
        {
            LastFlip = now.Add(FlipTime);
            return FirstAndSometimes;
        }
        return MostOfTheTime;
    }
}

Unit Test

Below is a unit test I wrote to ensure that it functions properly:

[Fact]
public void CanFlipBetweenValues()
{
    var timeToWait = TimeSpan.FromMilliseconds(10);

    var currentTime = DateTime.Now;
    var timeProvider = new Mock<ICurrentDateTimeProvider>();
    timeProvider.SetupGet(x => x.Now).Returns(currentTime);
    var cacher = new ReturnDifferentValueEverySoOften<bool>(false, true, timeToWait);
    cacher.SetTimeProvider(timeProvider.Object);

    Assert.False(cacher.Get());
    Assert.True(cacher.Get());
    Assert.True(cacher.Get());
    Assert.True(cacher.Get());
    Assert.True(cacher.Get());

    //add some arbitrary time > timeToWait
    var newTime = currentTime.Add(timeToWait).Add(new TimeSpan(0, 0, 1));
    var newTimeProvider = new Mock<ICurrentDateTimeProvider>();
    newTimeProvider.SetupGet(x => x.Now).Returns(newTime);
    cacher.SetTimeProvider(newTimeProvider.Object);

    Assert.False(cacher.Get());
    Assert.True(cacher.Get());
    Assert.True(cacher.Get());
    Assert.True(cacher.Get());
    Assert.True(cacher.Get());
}

Again, I'm mostly looking to see if there exists a better name for something like this, but I'm open to other code review suggestions as well.

\$\endgroup\$
  • 1
    \$\begingroup\$ martinfowler.com/bliki/TwoHardThings.html \$\endgroup\$ – jonrsharpe Jun 29 '15 at 15:27
  • \$\begingroup\$ It's a set/reset latch with a time delay as the reset? en.wikibooks.org/wiki/Electronics/… ; Is it intended to load the AD data once and return it, then return the previous AD data for a while (it's a cache)? Or to return a true/false "ok, you can reload now" like your unit test (it's a time delay)? An UpdateEscapement en.wikipedia.org/wiki/Escapement ? A musical sustain? A RefreshAndHold, a ReloadGovernor ( google.com/search?q=define:governor def 4), or a WorkloadRegulator ? Time delay. If (delay.HasPassed) { loadFromAD() } \$\endgroup\$ – TessellatingHeckler Jun 29 '15 at 18:27
  • \$\begingroup\$ basically in the real code it will work the same as in my unit test, it will "hold" a boolean value that will determine whether or not to load the AD data. so i do if(cacher.Get()) loadADData(); thanks for the suggestions \$\endgroup\$ – DLeh Jun 29 '15 at 18:31
  • \$\begingroup\$ it is definitely very similar to a Latch, i want it to hold a state, read it without it changing, and have the value change back under certain circumstances (in this case, it times out) \$\endgroup\$ – DLeh Jun 29 '15 at 18:34
  • \$\begingroup\$ i think for now i will call it a TimeSensitiveLatch. thanks for your input. \$\endgroup\$ – DLeh Jun 29 '15 at 18:41
1
\$\begingroup\$

I might be completely off with this, but I'll try to propose an alternative design for your problem.

It seems to me that you have two independent tasks in your application:

  1. Fetching the data from the DB (user triggered?, frequent)
  2. Updating the DB from the AD periodically (time consuming)

Define a flag in your application, say, UpdateInProgress. Update task would set it to true before running and reset it after it's done. Every Fetch request that comes while an update is in progress would be queued and executed after the update has completed.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.