具有类的功能Powershell-不是矛盾的,我保证

哈Ha! 我向您提供文章“带有类的功能PowerShell”的翻译
我保证这不是克里斯托弗·库奇(Christopher Kuech) 的矛盾


面向对象和函数式编程范例似乎相互矛盾,但Powershell均支持这两种范例。 几乎所有的编程语言,无论是功能性语言还是非功能性语言,都具有扩展名称和值的绑定的方式。 类(例如结构和记录)只是一种方法。 如果我们将类的使用限制为名称和值,并且避免使用诸如继承,多态性或可变性之类的“繁重”的面向对象编程概念,则可以在不使我们的代码复杂化的情况下利用它们。 此外,添加不可变的类型转换方法,我们可以使用类来丰富我们的功能代码。


种姓魔法


种姓是Powershell中最强大的功能之一。 转换值时,您依赖于环境向应用程序添加隐式初始化和验证的能力。 例如,在[xml]中对字符串进行简单的转换将通过解析器代码运行它并生成完整的xml树。 我们可以出于相同目的在代码中使用类。


自定义哈希表


如果您没有构造函数,则可以通过将哈希表强制转换为类的类型来继续使用。 记住要使用验证属性来充分利用此模式。 同时,我们可以使用类的类型化属性来触发更深层的初始化和验证逻辑。


class Cluster { [ValidatePattern("^[Az]+$")] [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的Cluster哈希表数组的输出与您首先将这些哈希表转换为类时发生的情况进行比较。 类属性始终按照在此定义的顺序列出。 不要忘记在所有在输出中不可见的属性之前添加hidden关键字。


图片

自订值


如果您有一个带有一个参数的构造函数,则将值转换为您的类类型会将值传递给此构造函数,您可以在其中初始化类的实例


 class Cluster { [ValidatePattern("^[Az]+$")] [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("^[Az]+$")] [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'" 

自定义序列化实例


演员表允许安全的反序列化。 如果数据不符合我们在集群中的规范,则以下示例将失败


 #    [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映射值数组。


在下面的示例中,每次对Datum进行强制转换时都会执行Node构造函数,这使我们不必编写大量代码。 结果,我们的管道专注于声明性数据查询和聚合,而我们的类负责数据解析和验证。


 #       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 "([az]+)(INT|PPE|PROD)([az]+)" $_, $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,并在那里定义文件中的所有类。


  • 您可以创建一个导出所有用户API(cmdlet)的Powershell模块,并在模块清单中设置变量ScriptsToProcess =“ ./my-classes.ps1”,结果相同:./my-classes.ps1将在您的环境中执行。



无论选择哪个选项,请记住Powershell类型系统无法解析从不同位置加载的相同名称的类型。
即使您从不同位置下载两个具有相同属性的相同类,也可能会遇到问题。


前进的道路


避免类型解析问题的最佳方法是永远不要向用户公开类。 不必期望用户导入在类中定义的类型,而是从模块中导出无需直接访问类的函数。 对于Cluster,我们可以导出New-Cluster函数,该函数将支持用户友好的参数集并返回Cluster。


 class Cluster { [ValidatePattern("^[Az]+$")] [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中的函数式编程

Source: https://habr.com/ru/post/zh-CN484300/


All Articles