Το λειτουργικό Powershell με κλάσεις δεν είναι οξύμωρο, το εγγυώμαι

Γεια σου Χαμπρ! Σας παρουσιάζω τη μετάφραση του άρθρου "Λειτουργικό PowerShell με τάξεις.
Υπόσχομαι ότι δεν είναι οξύμωρο»
από τον Christopher Kuech.

Τα αντικειμενοστραφή και λειτουργικά παραδείγματα προγραμματισμού μπορεί να φαίνονται σε αντίθεση μεταξύ τους, αλλά και τα δύο υποστηρίζονται εξίσου στο Powershell. Σχεδόν όλες οι γλώσσες προγραμματισμού, λειτουργικές ή μη, διαθέτουν δυνατότητες εκτεταμένης σύνδεσης ονόματος-τιμής. Οι τάξεις, όπως οι δομές και οι εγγραφές, είναι μόνο μία προσέγγιση. Εάν περιορίσουμε τη χρήση των Τάξεων στη δέσμευση ονομάτων και τιμών και αποφύγουμε τις έντονες αντικειμενοστρεφείς έννοιες προγραμματισμού, όπως η κληρονομικότητα, ο πολυμορφισμός ή η μεταβλητότητα, μπορούμε να εκμεταλλευτούμε τα πλεονεκτήματά τους χωρίς να περιπλέκουμε τον κώδικά μας. Επιπλέον, προσθέτοντας μεθόδους μετατροπής αμετάβλητου τύπου, μπορούμε να εμπλουτίσουμε τον λειτουργικό μας κώδικα με Τάξεις.

Η μαγεία των καστών

Οι κάστες είναι ένα από τα πιο ισχυρά χαρακτηριστικά του Powershell. Όταν μεταφέρετε μια τιμή, βασίζεστε στις σιωπηρές δυνατότητες αρχικοποίησης και επικύρωσης που προσθέτει το περιβάλλον στην εφαρμογή σας. Για παράδειγμα, η απλή χύτευση μιας συμβολοσειράς σε [xml] θα την τρέξει μέσω του κωδικού ανάλυσης και θα δημιουργήσει ένα πλήρες δέντρο xml. Μπορούμε να χρησιμοποιήσουμε Classes στον κώδικά μας για τον ίδιο σκοπό.

Cast hashtables

Εάν δεν έχετε κατασκευαστή, μπορείτε να συνεχίσετε χωρίς να μεταφέρετε ένα hashtable στον τύπο της τάξης σας. Μην ξεχάσετε να χρησιμοποιήσετε τα χαρακτηριστικά επικύρωσης για να επωφεληθείτε πλήρως από αυτό το μοτίβο. Ταυτόχρονα, μπορούμε να χρησιμοποιήσουμε τις ιδιότητες πληκτρολόγησης της κλάσης για να εκτελέσουμε ακόμη βαθύτερη λογική αρχικοποίησης και επικύρωσης.

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
}

Επιπλέον, η χύτευση βοηθά να έχετε καθαρό αποτέλεσμα. Συγκρίνετε την έξοδο του πίνακα κατακερματισμού του συμπλέγματος που μεταβιβάστηκε στο Format-Table με αυτό που λαμβάνετε εάν πρώτα μεταφέρετε αυτά τα hashtable σε μια κλάση. Οι ιδιότητες μιας κλάσης παρατίθενται πάντα με τη σειρά που ορίζονται εκεί. Μην ξεχάσετε να προσθέσετε την κρυφή λέξη-κλειδί πριν από όλες εκείνες τις ιδιότητες που δεν θέλετε να είναι ορατές στα αποτελέσματα.

Το λειτουργικό Powershell με κλάσεις δεν είναι οξύμωρο, το εγγυώμαι

Κάστ των νοημάτων

Εάν έχετε έναν κατασκευαστή με ένα όρισμα, η μετάδοση μιας τιμής στον τύπο της κλάσης σας θα μεταβιβάσει την τιμή στον κατασκευαστή σας, όπου μπορείτε να αρχικοποιήσετε μια παρουσία της κλάσης σας

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"

Μετάδοση στη γραμμή

Μπορείτε επίσης να παρακάμψετε τη μέθοδο κλάσης [string] ToString() για να ορίσετε τη λογική πίσω από την αναπαράσταση συμβολοσειράς του αντικειμένου, όπως η χρήση παρεμβολής συμβολοσειρών.

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'"

Παρουσίαση σειριακών περιπτώσεων

Το Cast επιτρέπει την ασφαλή αποζωνοποίηση. Τα παρακάτω παραδείγματα θα αποτύχουν εάν τα δεδομένα δεν πληρούν τις προδιαγραφές μας στο Cluster

# Валидация сериализованных данных

[Cluster]$cluster = Get-Content "./my-cluster.json" | ConvertFrom-Json
[Cluster[]]$clusters = Import-Csv "./my-clusters.csv"

Κάστα στον λειτουργικό σας κώδικα

Τα λειτουργικά προγράμματα ορίζουν πρώτα τις δομές δεδομένων και στη συνέχεια εφαρμόζουν το πρόγραμμα ως μια ακολουθία μετασχηματισμών σε αμετάβλητες δομές δεδομένων. Παρά την αντιφατική εντύπωση, οι κλάσεις σας βοηθούν πραγματικά να γράψετε λειτουργικό κώδικα χάρη στις μεθόδους μετατροπής τύπων.

Είναι λειτουργικό το Powershell που γράφω;

Πολλοί άνθρωποι που προέρχονται από C# ή παρόμοιο υπόβαθρο γράφουν Powershell, το οποίο είναι παρόμοιο με το C#. Κάνοντας αυτό, απομακρύνεστε από τη χρήση των εννοιών λειτουργικού προγραμματισμού και πιθανότατα θα επωφεληθείτε από την έντονη κατάδυση στον αντικειμενοστραφή προγραμματισμό στο Powershell ή να μάθετε περισσότερα για τον λειτουργικό προγραμματισμό.

Εάν βασίζεστε σε μεγάλο βαθμό στον μετασχηματισμό αμετάβλητων δεδομένων χρησιμοποιώντας αγωγούς (|), Where-Object, ForEach-Object, Select-Object, Group-Object, Sort-Object, κ.λπ. - έχετε ένα πιο λειτουργικό στυλ και θα επωφεληθείτε από τη χρήση του Powershell μαθήματα σε λειτουργικό στυλ.

Λειτουργική χρήση τάξεων

Οι κάστες, αν και χρησιμοποιούν μια εναλλακτική σύνταξη, είναι απλώς μια αντιστοίχιση μεταξύ δύο τομέων. Στη διοχέτευση, μπορείτε να αντιστοιχίσετε μια σειρά τιμών χρησιμοποιώντας το ForEach-Object.

Στο παρακάτω παράδειγμα, ο κατασκευαστής Node εκτελείται κάθε φορά που εκπέμπεται ένα Datatum, και αυτό μας δίνει την ευκαιρία να αποφύγουμε τη σύνταξη αρκετού κώδικα. Ως αποτέλεσμα, η διοχέτευσή μας εστιάζει στην αναζήτηση και τη συγκέντρωση δηλωτικών δεδομένων, ενώ οι τάξεις μας φροντίζουν για την ανάλυση και την επικύρωση δεδομένων.

# Пример комбинирования классов с конвейерами для 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 -Unique

Κατηγορία συσκευασίας για επαναχρησιμοποίηση

Τίποτα δεν είναι τόσο καλό όσο φαίνεται

Δυστυχώς, οι κλάσεις δεν μπορούν να εξαχθούν από μονάδες με τον ίδιο τρόπο όπως οι συναρτήσεις ή οι μεταβλητές. αλλά υπάρχουν μερικά κόλπα. Ας υποθέσουμε ότι οι τάξεις σας ορίζονται στο αρχείο ./my-classes.ps1

  • Μπορείτε να προμηθεύσετε ένα αρχείο με κλάσεις:. ./my-classes.ps1. Αυτό θα εκτελέσει το my-classes.ps1 στο τρέχον πεδίο εφαρμογής σας και θα ορίσει όλες τις κλάσεις από το αρχείο εκεί.

  • Μπορείτε να δημιουργήσετε μια λειτουργική μονάδα Powershell που εξάγει όλα τα προσαρμοσμένα API (cmdlet) σας και να ορίσετε τη μεταβλητή ScriptsToProcess = "./my-classes.ps1" στο μανιφέστο της ενότητας, με το ίδιο αποτέλεσμα: ./my-classes.ps1 θα εκτελεστεί σε το περιβάλλον σας.

Όποια επιλογή κι αν επιλέξετε, να θυμάστε ότι το σύστημα τύπων του Powershell δεν μπορεί να επιλύσει τύπους με το ίδιο όνομα που έχουν φορτωθεί από διαφορετικά μέρη.
Ακόμα κι αν έχετε φορτώσει δύο ίδιες κλάσεις με τις ίδιες ιδιότητες από διαφορετικά μέρη, κινδυνεύετε να αντιμετωπίσετε προβλήματα.

Ο δρόμος μπροστά

Ο καλύτερος τρόπος για να αποφύγετε προβλήματα επίλυσης τύπων είναι να μην εκθέτετε ποτέ τις τάξεις σας στους χρήστες. Αντί να περιμένετε από τον χρήστη να εισαγάγει έναν τύπο που ορίζεται από την κλάση, εξάγετε μια συνάρτηση από τη λειτουργική μονάδα σας που εξαλείφει την ανάγκη άμεσης πρόσβασης στην κλάση. Για το Cluster, μπορούμε να εξαγάγουμε μια συνάρτηση New-Cluster που θα υποστηρίζει φιλικά προς το χρήστη σύνολα παραμέτρων και θα επιστρέψει ένα 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

Τι άλλο να διαβάσετε

Σχετικά με τα μαθήματα
Αμυντικό PowerShell
Λειτουργικός Προγραμματισμός στο PowerShell

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο