I have read in various places that Global variables are at best a code smell, and best avoided. At the moment I am working on refactoring a big function based PS script to classes, and thought to use a Singleton. The use case being a large data structure that will need to be referenced from a lot of different classes and modules. Then I found this, which seems to suggest that Singletons are a bad idea too.
So, what IS the right way (in PS 5.1) to create a single data structure that needs to be referenced by a lot of classes, and modified by some of them? Likely pertinent is the fact that I do NOT need this to be thread safe. By definition the queue will be processed in a very linear fashion.
FWIW, I got to the referenced link looking for information on singletons and inheritance, since my singleton is simply one of a number of classes with very similar behavior, where I start with the singleton which contains collections of the next class, which each contain collections of the next class, to create a hierarchical queue. I wanted to have a base class that handled all the common queue management then extend that for the differing functionality lof each class. Which works great other than having that first extended class be a singleton. That seems to be impossible, correct?
EDIT: Alternatively, is it possible with this nested classes in a generic list property approach to be able to identify the parent from within a child? This is how I handled this is the Function based version. A global [XML]
variable formed the data structure, and I could step through that structure, using .SelectNode()
to populate a variable to pass to the next function down, and using .Parent
to get information from higher up, and especially from the root of the data structure.
EDIT: Since I seem not to be able to paste code here right now, I have some code on GitHub. The example here of where the Singleton comes in is at line 121, where I need to verify if there are any other examples of the same task that have not yet comnepelted, so I can skip all but the last instance. This is a proof of concept for deleting common components of various Autodesk software, which is managed in a very ad hoc manner. So I want to be able to install any mix of programs (packages) and uninstall on any schedule, and ensure that the last package that has a shared component uninstall is the one that uninstalls it. So as to no break other dependent programs before that last uninstall happens. Hopefully that makes sense. Autodesk installs are a fustercluck of misery. If you don't have to deal with them, consider yourself lucky. :)
As mentioned in the comments, nothing in the code you linked to requires a singleton.
If you want to retain a parent-child relationship between your ProcessQueue
and related Task
instance, that can be solved structurally.
Simply require injection of a ProcessQueue
instance in the Task
constructor:
class ProcessQueue
{
hidden [System.Collections.Generic.List[object]]$Queue = [System.Collections.Generic.List[object]]::New()
}
class Task
{
[ProcessQueue]$Parent
[string]$Id
Task([string]$id, [ProcessQueue]$parent)
{
$this.Parent = $parent
$this.Id = $id
}
}
When instantiating the object hierarchy:
$myQueue = [ProcessQueue]::new()
$myQueue.Add([Task]@{ Id = "id"; Parent = $myQueue})
... or refactor ProcessQueue.Add()
to take care of constructing the task:
class ProcessQueue
{
[Task] Add([string]$Id){
$newTask = [Task]::new($Id,$this)
$Queue.Add($newTask)
return $newTask
}
}
At which point you just use ProcessQueue.Add()
as a proxy for the [Task]
constructor:
$newTask = $myQueue.Add($id)
$newTask.DisplayName = "Display name goes here"
Next time you need to search related tasks from a single Task
instance, you just do:
$relatedTasks = $task.Parent.Find($whatever)