First things fist: the "Pavarotti-String" design pattern, is not a design pattern :). It's more of a programming practice; and the idea was pitched to me about 3 or 4 years ago. But it can be a real pain if it's ignored in large code bases; and a solution can be applied pretty easily.
Calling it the "Pavarotti-String Design Pattern" is a simple but effective trick to help you remember implementing it. But more on that later :).
Strings everywhere
It's a common mistake. Having strings placed throughout your code. Think of names of connection-string, queue names, table names, etc.
Floating string values scattered trough your code-base will impair your ability to refactor your code efficiently. Worst case should one of those strings need to change? Find-replace..
Let us look at the following code sample:
await _servicebusSenderClient.GetMessageSender("superduper-datapersistence-queue")
.SendAsync(message);
exporter.Verify(c => c.ExportToCrmAsync(It.Is<CrmContract>(x =>
x.Component == "aDefaultComponent" &&
x.Process == "theDefaultProcess" &&
x.Type == "domeDefaultType" &&
x.Label == "SomeLabel"
), string.Empty));
The MessageBus
can be used as a primary example. The first line of code sends a message to the "superduper-datapersistence-queue"
queue.
The main problem with using a string like this is that you can only search your entire code base for that string to see where something is put on that queue.
And if you want to change the queue name; you can only use 'Find-Replace'. In modern programming techniques that is a bad practice; and violates SOLID principles.
The same goes for some of the string values in the second line. The constant (or default in these cases) values are used to get a set of objects that matches the parameters. The x.label
is not a default one; and as such; we will not touch it.
Fixing it
Fixing is fairly easy. We move the default string values into a separate static Constants
class.
public static class Constants
{
public static class QueueNames
{
public const string DatapersistenceQueue = "superduper-datapersistence-queue";
public const string SomeOtherQueue = "someother-queue";
}
public static class StringValues
{
public const string Unspecified = "NotSpecified";
public const string DefaultComponent = "aDefaultComponent";
public const string DefaultProcess = "theDefaultProcess";
public const string Type = "domeDefaultType";
}
}
This now enables us to use Constants.QueueNames.DatapersistenceQueue
instead of the string value. Same goes for the StringValues
for default values.
Our code sample will then look like this:
await _servicebusSenderClient.GetMessageSender(Constants.QueueNames.DatapersistenceQueue)
.SendAsync(message);
exporter.Verify(c => c.ExportToCrmAsync(It.Is<CrmContract>(x =>
x.Component == Constants.StringValues.DefaultComponent &&
x.Process == Constants.StringValues.DefaultProcess &&
x.Type == Constants.StringValues.DefaultType &&
x.Label == "SomeLabel"
), string.Empty));
The advantages should be obvious. You can simply change a string value in the Constants
class, and the change is incorporated in your whole code base. It also results in much cleaner code. You can do safe refactoring and you can move away from the "Find/Replace" functionality.
This also works for value-types:
var dbMax = Constants.DbValues.MaxSelectCount;
var pageCount = Constants.Paging.PageCount;
Pavarotti?
So why call it the "Pavarotti-design pattern"? It is actually meant as a joke; and provides a simple way for remembering it.
Pavarotti was an Italian Opera legend. I mean absolutely no harm or offense; he was one of the greatest opera singers ever!
But, he was also quite large..
The idea is that every time you see a floating string value in your code-base; imagine Pavarotti in a string (yes, the underpants in this case). That's how much it should hurt if your see string value violations. And the mental image should motivate you to apply a fix immediately ;).
Hope you enjoyed the read; happy coding !
Photo by Fotis Fotopoulos on Unsplash.
Comments?
Leave us your opinion.