Ahoj, Habr! Představuji vám překlad článku autora Christophera Kuecha.
Objektově orientované a funkční programovací paradigmy se mohou zdát neslučitelné, ale obě jsou v PowerShellu rovnocenně podporovány. Prakticky všechny programovací jazyky, funkční i ne, mají prostředky pro pokročilé vazby jmen a hodnot; Třídy, podobně jako struktury a záznamy, jsou jen jedním přístupem. Pokud omezíme použití tříd na vazby jmen a hodnot a vyhneme se "těžkým" konceptům objektově orientovaného programování, jako jsou dědičnost, polymorfismus či mutabilita, můžeme jejich výhody využít, aniž bychom zbytečně komplikovali náš kód. Dále, přidáním neměnných metod pro převod typů můžeme obohatit náš funkční kód o třídy.
Kouzlo přetypování
Přetypování je jedna z nejmocnějších funkcí v PowerShellu. Když hodnotu přetypujete, spoléháte se na implicitní inicializaci a validaci, které prostředí do vaší aplikace přidává. Například jednoduché přetypování řetězce na [xml] projde kódem parseru a vygeneruje kompletní XML strom. Ve svém kódu můžeme použít třídy ke stejnému účelu.
Přetypování hashovacích tabulek
Pokud nemáte konstruktor, můžete pokračovat bez něj a použít přetypování hashovací tabulky na typ vaší třídy. Nezapomeňte využít validační atributy, abyste plně využili tohoto vzoru. Přitom můžeme použít typizované vlastnosti třídy, abychom spustili ještě hlubší logiku inicializace a validace.
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
}Kromě toho přetypování pomáhá získat čistý výstup. Porovnejte výstup pole hashovacích tabulek Cluster předaných do Format-Table s tím, co získáte, pokud předtím přetypujete tyto hash tabulky na třídu. Vlastnosti třídy jsou vždy vypsány v pořadí, v jakém byly definovány. Nezapomeňte přidat klíčové slovo hidden před všechny ty vlastnosti, které by neměly být viditelné ve výstupu.

Přetypování hodnot
Pokud máte konstruktor s jedním argumentem, převod hodnot na váš typ třídy předá hodnotu tomuto vašemu konstruktoru, ve kterém můžete inicializovat instanci vaší třídy.
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"Převedení na řetězec
Také můžete přepsat metodu třídy [string] ToString(), abyste definovali logiku pro řetězcovou reprezentaci objektu, například použít interpolaci řetězců.
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 "Právě jsme vytvořili model pro '$cluster'"Převedení serializovaných instancí
Převod umožňuje bezpečnou deserializaci. Příklady níže skončí chybou, pokud data neodpovídají naší specifikaci v Cluster.
# Валидация сериализованных данных
[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"Převody ve vašem funkcionálním kódu
Funkcionální programy nejprve definují datové struktury, pak implementují program jako sekvenci transformací na neměnných datových strukturách. Ačkoli to může působit protichůdně, třídy skutečně pomáhají psát funkcionální kód díky metodám konverze typů.
Píšu Powershell funkcionálně?
Mnoho lidí, kteří přišli z C# nebo se podobnou minulostí, píše Powershell, který se podobá C#. Tímto způsobem se vzdáváte používání konceptů funkcionálního programování a pravděpodobně získáte prospěch, když se více ponoříte do objektově orientovaného programování v Powershell nebo se lépe naučíte funkcionální programování.
Pokud se hluboce spoléháte na transformaci neměnných dat pomocí potrubí (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object atd. — máte funkcionálnější styl a pomohou vám třídy Powershell ve funkcionálním stylu.
Funkcionální použití tříd
Klastrování, i když používá alternativní syntaxi, je pouze mapování mezi dvěma doménami. V pipeline můžete mapovat pole hodnot pomocí ForEach-Object.
V následujícím příkladu se konstruktor Node vykonává pokaždé, když dochází k převodu na Datum, což nám dává možnost nenapsat značné množství kódu. V důsledku toho se naše pipeline zaměřuje na deklarativní dotazování dat a agregaci, zatímco naše třídy se starají o analýzu a validaci dat.
# Пример комбинирования классов с конвейерами для 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 -UniqueBalení třídy pro opětovné použití
Nic není tak dobré, jak se zdá
Bohužel, třídy nemohou být exportovány moduly stejným způsobem jako funkce nebo proměnné; ale existují určité triky. Předpokládejme, že vaše třídy jsou definovány v souboru ./my-classes.ps1.
Můžete provést dot-source souboru s třídami: . ./my-classes.ps1. To vykoná my-classes.ps1 v aktuální oblasti viditelnosti a určí všechny třídy ze souboru.
Můžete vytvořit modul Powershell, který exportuje všechny vaše uživatelské API (cmdlety) a nastavit proměnnou ScriptsToProcess = "./my-classes.ps1" v manifestu vašeho modulu, se stejným výsledkem: ./my-classes.ps1 se vykoná ve vašem prostředí.
Bez ohledu na to, kterou možnost vyberete, nezapomeňte, že systém typů Powershell nedokáže rozlišit stejné typy načítané z různých míst.
I když jste načetli ze různých míst dvě identické třídy se stejnými vlastnostmi, můžete narazit na problémy.
Cesta vpřed
Nejlepším způsobem, jak se vyhnout problémům s rozlišováním typů, je nikdy nenabízet své třídy uživatelům. Místo očekávání, že uživatel importuje konkrétní typ v třídě, exportujte z vašeho modulu funkci, která uvolní potřebu přímo se obracet na třídu. V případě Clusteru můžeme exportovat funkci New-Cluster, která podpoří uživatelsky přívětivé sady parametrů a vrátí Cluster.
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-ClusterCo si ještě přečíst
Zdroj: habr.com
