Hey Habr! Ich präsentiere Ihnen die Übersetzung des Artikels von Christopher Kuech.
Objektorientierte und funktionale Programmierparadigmen scheinen einander zu widersprechen, aber beide werden in Powershell gleichermaßen unterstützt. Fast alle Programmiersprachen, ob funktional oder nicht, verfügen über Funktionen zur erweiterten Bindung von Namen und Werten. Klassen sind, wie Strukturen und Datensätze, nur ein Ansatz. Wenn wir die Verwendung von Klassen auf die Bindung von Namen und Werten beschränken und „schwere“ Konzepte der objektorientierten Programmierung wie Vererbung, Polymorphismus oder Veränderlichkeit vermeiden, können wir ihre Vorteile nutzen, ohne unseren Code zu verkomplizieren. Als Nächstes können wir unseren Funktionscode mit Klassen anreichern, indem wir unveränderliche Methoden zur Typkonvertierung hinzufügen.
Magie der Kasten
Casts sind eine der leistungsstärksten Funktionen in Powershell. Wenn Sie einen Wert umwandeln, verlassen Sie sich auf die impliziten Initialisierungs- und Validierungsfunktionen, die die Umgebung Ihrer Anwendung hinzufügt. Wenn Sie beispielsweise eine Zeichenfolge einfach in [xml] umwandeln, wird sie durch den Parsercode ausgeführt und ein vollständiger XML-Baum generiert. Wir können in unserem Code Klassen für denselben Zweck verwenden.
Cast-Hashtabellen
Wenn Sie keinen Konstruktor haben, können Sie ohne einen fortfahren, indem Sie die Hashtabelle in Ihren Klassentyp umwandeln. Verwenden Sie unbedingt Validierungsattribute, um dieses Muster optimal zu nutzen. Gleichzeitig können wir die typisierten Eigenschaften der Klasse verwenden, um eine noch tiefere Initialisierungs- und Validierungslogik auszuführen.
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
}
[Cluster]@{
Service = "MyService"
FlightingRing = "PROD"
Region = "EastUS"
Index = 2
}Darüber hinaus trägt das Gießen dazu bei, ein sauberes Ergebnis zu erzielen. Vergleichen Sie die Ausgabe des an Format-Table übergebenen Cluster-Hashtabellen-Arrays mit dem, was Sie erhalten, wenn Sie diese Hashtabellen zuerst in eine Klasse umwandeln. Die Eigenschaften einer Klasse werden immer in der Reihenfolge aufgelistet, in der sie dort definiert sind. Vergessen Sie nicht, vor allen Eigenschaften, die in den Suchergebnissen nicht sichtbar sein sollen, das versteckte Schlüsselwort hinzuzufügen.

Wertebesetzung
Wenn Sie einen Konstruktor mit einem Argument haben, wird beim Umwandeln eines Werts in Ihren Klassentyp der Wert an Ihren Konstruktor übergeben, in dem Sie eine Instanz Ihrer Klasse initialisieren können.
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
Cluster([string] $id) {
$this.Service, $this.FlightingRing, $this.Region, $this.Index = $id -split "-"
}
}
[Cluster]"MyService-PROD-EastUS-2"Auf Linie gießen
Sie können auch die ToString()-Methode der Klasse [string] überschreiben, um die Logik für die Darstellung des Objekts in einem String zu definieren, beispielsweise durch die Verwendung einer String-Interpolation.
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
[string] ToString() {
return $this.Service, $this.FlightingRing, $this.Region, $this.Index -join "-"
}
}
$cluster = [Cluster]@{
Service = "MyService"
FlightingRing = "PROD"
Region = "EastUS"
Index = 2
}
Write-Host "We just created a model for '$cluster'"Casting serialisierter Instanzen
Cast ermöglicht eine sichere Deserialisierung. Die folgenden Beispiele schlagen fehl, wenn die Daten nicht unserer Spezifikation im Cluster entsprechen
# Валидация сериализованных данных
[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"Casts in Ihrem Funktionscode
Funktionale Programme definieren zunächst Datenstrukturen und implementieren das Programm dann als eine Abfolge von Transformationen auf unveränderlichen Datenstrukturen. Auch wenn es zunächst nicht intuitiv erscheint, helfen Ihnen Klassen dank ihrer Methoden zur Typkonvertierung tatsächlich dabei, funktionalen Code zu schreiben.
Schreibe ich funktionales Powershell?
Viele Leute mit C#- oder ähnlichem Hintergrund schreiben Powershell, das C# ähnelt. Auf diese Weise verzichten Sie auf die Verwendung funktionaler Programmierkonzepte und würden wahrscheinlich davon profitieren, tiefer in die objektorientierte Programmierung in Powershell einzutauchen oder mehr über funktionale Programmierung zu lernen.
Wenn Sie sich stark auf die Transformation unveränderlicher Daten mithilfe von Pipes (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object usw. verlassen, haben Sie einen eher funktionalen Stil und profitieren von der Verwendung von Powershell-Klassen in einem funktionalen Stil.
Funktionale Verwendung von Klassen
Obwohl Kasten eine alternative Syntax verwenden, sind sie lediglich eine Zuordnung zwischen zwei Domänen. In einer Pipeline können Sie mithilfe von ForEach-Object ein Array von Werten abbilden.
Im folgenden Beispiel wird der Knotenkonstruktor jedes Mal ausgeführt, wenn ein Datum umgewandelt wird, was uns eine Menge Code spart. Daher konzentriert sich unsere Pipeline auf die deklarative Datenabfrage und -aggregation, während unsere Klassen die Datenanalyse und -validierung übernehmen.
# Пример комбинирования классов с конвейерами для separation of concerns в конвейерах
class Node {
[ValidateLength(3, 7)]
[string] $Name
[ValidateSet("INT", "PPE", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope", "WestEurope")]
[string] $Region
Node([string] $Name) {
$Name -match "([a-z]+)(INT|PPE|PROD)([a-z]+)"
$_, $this.Service, $this.FlightingRing, $this.Region = $Matches
$this.Name = $Name
}
}
class Datum {
[string] $Name
[int] $Value
[Node] $Computer
[int] Severity() {
$this.Name -match "[0-9]+$"
return $Matches[0]
}
}
Write-Host "Urgent Security Audit Issues:"
Import-Csv "./audit-results.csv" `
| ForEach-Object {[Datum]$_} `
| Where-Object Value -gt 0 `
| Group-Object {$_.Severity()} `
| Where-Object Name -lt 2 `
| ForEach-Object Group `
| ForEach-Object Computer `
| Where-Object FlightingRing -eq "PROD" `
| Sort-Object Name, Region -UniqueRecycelbare Klasse-Verpackung
Nichts ist so gut wie es scheint
Leider können Klassen nicht auf die gleiche Weise wie Funktionen oder Variablen von Modulen exportiert werden. aber es gibt einige Tricks. Angenommen, Ihre Klassen sind in der Datei ./my-classes.ps1 definiert
Sie können die Datei mit folgenden Klassen als Dotsource bereitstellen:. ./meine-klassen.ps1. Dadurch wird my-classes.ps1 in Ihrem aktuellen Bereich ausgeführt und alle Klassen aus der Datei dort definiert.
Sie können ein Powershell-Modul erstellen, das alle Ihre benutzerdefinierten APIs (Cmdlets) exportiert, und die Variable ScriptsToProcess = "./my-classes.ps1" im Manifest Ihres Moduls festlegen, mit demselben Ergebnis: ./my-classes.ps1 wird in Ihrer Umgebung ausgeführt.
Egal für welche Option Sie sich entscheiden, denken Sie daran, dass das Powershell-Typsystem keine Typen mit demselben Namen auflösen kann, die von verschiedenen Orten geladen wurden.
Selbst wenn Sie zwei identische Klassen mit denselben Eigenschaften von verschiedenen Orten laden, besteht die Gefahr, dass Probleme auftreten.
Weiter so
Die beste Möglichkeit, Probleme mit der Typauflösung zu vermeiden, besteht darin, Ihre Klassen niemals Benutzern zugänglich zu machen. Anstatt vom Benutzer zu erwarten, dass er einen in einer Klasse definierten Typ importiert, exportieren Sie eine Funktion aus Ihrem Modul, die den direkten Zugriff auf die Klasse überflüssig macht. Für Cluster können wir eine New-Cluster-Funktion exportieren, die benutzerfreundliche Parametersätze unterstützt und einen Cluster zurückgibt.
class Cluster {
[ValidatePattern("^[A-z]+$")]
[string] $Service
[ValidateSet("TEST", "STAGE", "CANARY", "PROD")]
[string] $FlightingRing
[ValidateSet("EastUS", "WestUS", "NorthEurope")]
[string] $Region
[ValidateRange(0, 255)]
[int] $Index
}
function New-Cluster {
[OutputType([Cluster])]
Param(
[Parameter(Mandatory, ParameterSetName = "Id", Position = 0)]
[ValidateNotNullOrEmpty()]
[string] $Id,
[Parameter(Mandatory, ParameterSetName = "Components")]
[string] $Service,
[Parameter(Mandatory, ParameterSetName = "Components")]
[string] $FlightingRing,
[Parameter(Mandatory, ParameterSetName = "Components")]
[string] $Region,
[Parameter(Mandatory, ParameterSetName = "Components")]
[int] $Index
)
if ($Id) {
$Service, $FlightingRing, $Region, $Index = $Id -split "-"
}
[Cluster]@{
Service = $Service
FlightingRing = $FlightingRing
Region = $Region
Index = $Index
}
}
Export-ModuleMember New-ClusterWas gibt es sonst noch zu lesen?
Source: habr.com
