Përshëndetje, Habr! Ju prezantoj përkthimin e artikullit autori Christopher Kuech.
Paradigmat e programimit objektiv dhe funksional mund të duken se nuk janë në harmoni me njëra-tjetrën, por të dyja përkrahen në një masë të barabartë në Powershell. Praktikisht të gjitha gjuhët e programimit, funksionale dhe jo, kanë mjete për lidhje të avancuara të emrave dhe vlerave; Klasa, siç janë struct-at dhe rekordet, është vetëm një qasje. Nëse e kufizojmë përdorimin e Klasa në lidhjen e emrave dhe vlerave dhe shmangim konceptet e këtij programimi objektiv "të r heavy", si trashëgimia, polimorfizmi, apo ndryshueshmëria (mutability), mund të shfrytëzojmë përfitimet e tyre pa e komplikuar kodin tonë. Më tej, duke shtuar metoda të pandryshueshme (immutable) për konvertimin e tipeve, mund të pasurojmë kodin tonë funksional me Klasa.
Magjia e kastave
Kastimi është një nga funksionet më të fuqishme në Powershell. Kur ju bëni kast një vlerë, ju mbështeteni në mundësitë që mjedisi e shton në aplikacionin tuaj për inicializim dhe validim implicit. Për shembull, një kast i thjeshtë i një stringu në [xml] do ta kalojë atë përmes kodit të parser-it dhe do të gjenerojë një pemë të plotë xml. Ne mund të përdorim në kodin tonë klasat me të njëjtin qëllim.
Kasti i hash tabelave
Nëse nuk keni ndërtues, mund të vazhdoni pa të, duke përdorur kastin e hash tabelave në tipin e klasës tuaj. Mos harroni të përdorni atributet e validimit për të shfrytëzuar plotësisht këtë model. Po ashtu, mund të përdorim pronat e tipizuara të klasës për të aktivizuar logjikën e thelluar të inicializimit dhe validimit.
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
}Për më tepër, hedhja ndihmon në marrjen e një输出 të pastër. Krahasoni daljen e një массив хэштаблиц Cluster të dërguar në Format-Table me atë që do të merrni nëse më parë i hedhni këto хэштаблицы në klasë. Pronat e klasës gjithmonë renditen në rendin në të cilin janë përcaktuar. Mos harroni të shtoni fjalën kyçe hidden përpara të gjitha atyre pronave që nuk duhet të jenë të dukshme në dalje.

Hedhja e vlerave
Nëse keni një ndërtues me një argument, hedhja e vlerës në tipin tuaj të klasës do të kalojë vlerën në atë ndërtuesin tuaj, ku mund të inicializoni instancën e klasës tuaj.
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"Hedhja në string
Gjithashtu, mund të mbivendosni metodën e klasës [string] ToString(), për të përcaktuar logjikën e paraqitjes së objektit si string, për shembull duke përdorur interpolimin e stringjeve.
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 "Ne kemi krijuar një model për '$cluster'"Kasti i instancave të serializuara
Kasti lejon deserializimin e sigurt. Shembujt në vazhdim do të përfundojnë me një gabim nëse të dhënat nuk përputhen me specifikimet tona në Cluster
# Валидация сериализованных данных
[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"Kasht në kodin tuaj funksional
Programet funksionale fillimisht përcaktojnë struktura të dhënash dhe pastaj zbatojnë programin si një sekuencë transformimesh mbi struktura të dhënash të pandryshueshme. Pavarësisht impresionit të kundërt, klasat me të vërtetë ndihmojnë në shkrimin e kodit funksional, falë metodave të konvertimit të tipeve.
A po shkruaj një Powershell funksional?
Shumë njerëz që vijnë nga C# ose kanë një të kaluar të ngjashme shkruajnë Powershell, i cili është i ngjashëm me C#. Duke bërë kështu, ju heqni dorë nga përdorimi i koncepteve të programimit funksional dhe, ndoshta, do të përfitoni duke u thelluar në programimin orientuar në objekte në Powershell ose duke mësuar më mirë programimin funksional.
Nëse ju mbështetni në thelb në transformimin e të dhënave të pandryshueshme, duke përdorur tubacione (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object etj. — ju keni një stil më funksional, dhe do t'ju ndihmojë përdorimi i klasave Powershell në stil funksional.
Përdorimi funksional i klasave
Kastet, edhe pse përdorin një sintaksë alternative, janë thjesht një hartëzim midis dy domain-eve. Në tubacion mund të maponi një array të vlerave, duke përdorur ForEach-Object.
Në shembullin më poshtë, konstruktorit Node i jepet ekzekutimi sa herë që bëhet kast në Datum, dhe kjo na jep mundësinë të mos shkruajmë një sasi të madhe kodi. Si rezultat, tubacioni ynë fokusohet në kërkesën deklarative të të dhënave dhe agregimin, ndërsa klasat tona marrin përsipër parsing-in e të dhënave dhe validimin.
# Пример комбинирования классов с конвейерами для 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 -UniquePaket i klasës për ripërdorim
Asgjë nuk është kaq e mirë sa duket
Fatkeqësisht, klasat nuk mund të eksportohen nga modulat në të njëjtën mënyrë si funksionet ose variablat; por ka disa truqe. Le të themi se klasat tuaja janë të përcaktuara në skedarin ./my-classes.ps1
Ju mund të bëni dote sourcing të skedarit me klasat: ./my-classes.ps1. Kjo do të ekzekutojë my-classes.ps1 në fushën tuaj aktuale dhe do të përcaktojë të gjitha klasat nga skedari.
Ju mund të krijoni një modul Powershell që eksporton të gjitha API-të tuaja të personalizuara (cmdlet) dhe të vendosni variablën ScriptsToProcess = "./my-classes.ps1" në manifestin e modulit tuaj, me të njëjtin rezultat: ./my-classes.ps1 do të ekzekutohet në ambientin tuaj.
Cilëndo opsion që zgjidhni, mos harroni se sistemi i tipave Powershell nuk mund të zgjidhë llojet me emra të njëjtë, të ngarkuara nga vende të ndryshme.
Edhe nëse keni ngarkuar nga vende të ndryshme dy klasa identike me pronësi të njëjta, rrezikoni të hasni probleme.
Rruga përpara
Mënyra më e mirë për të shmangur problemet me përcaktimin e tipeve është të mos ekspozoni klasat tuaja për përdoruesit. Në vend që të prisni që përdoruesi të importojë një tip të caktuar nga klasa, eksporto një funksion nga moduli juaj që heq nevojën për të kontaktuar drejtpërdrejt me klasën. Në lidhje me Cluster, mund të eksportohet funksioni New-Cluster, i cili do të mbështesë grupe miqësore për përdoruesit të parametrave dhe do të kthejë 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-ClusterÇfarë tjetër për të lexuar
Burimi: habr.com
