【问题标题】:powershell access sibling methodpowershell 访问兄弟方法
【发布时间】:2019-07-18 00:25:58
【问题描述】:

我无法在 powershell 中访问同级方法

$group.search.has_member(search)得到成员后是一样的,但是 $group.retrieve.Members() 获取 365 组与本地组的成员的方式不同

所以我试图让.search.has_member(search) 调用.retrieve.Members() 以最大程度地减少代码重复(IRL 大约有 15 个不同的版本,其中一些具有独特的检索方法,一些具有共享方法)。但我无法访问兄弟的方法

我的主要目标是尝试构建一个界面外观,以统一您与 Exchange 邮件对象的交互方式。 365 mailboxeslocal mailboxes365 groupslocal groups(以及其他)都可以拥有 MembersMemberOfEmail AddressesDistinguished Name,但 DN 是所有对象的属性, Email addresses 属性在 365 和本地组上的格式不同(这会影响 .MatchesSMTP($search)),并且对于 365 组和本地组,检索 Members() 是不同的,并且无论是 365 还是本地组,邮箱和邮件联系人都应该返回 null ,并且 MemberOf 的检索对于每种对象类型都是唯一的。

这会导致相当程度的复杂性。 Originally 我试图首先基于类型(邮箱 vs 组)然后基于服务器(mailbox_365mailbox_local 等)使用继承来打破它们,但我最终得到了太多重复的代码。交换订单时同样的问题(365_mailbox365_group 等)。

所以现在我正在尝试基于行为实现抽象,以便在可能的情况下更好地共享代码,并为需要更独特方法的版本分别选择样式/策略。但是.Searches 无法访问.Retrievals

我想我可以将retrievals 作为参数传递给searches 构造函数,就像我为config 传递的那样,但是如果我开始以更多具有交错依赖的行为类别结束,这种方法就不能很好地扩展。此外,如果我可以让同级访问正常工作,我可以停止将配置作为参数传递并直接访问它们,这应该会进一步清理代码。

下面是一个简化的示例供参考(是的,尽管它很复杂,但它已经大大降低了,我上次统计时最终版本大约有 26 个类)

Class Mail_Factory
    {
    static [Mail_Interface] BuildExample1()
        {
        $mail_object = "Pretend 365 group"
        return [Mail_Factory]::Build($mail_object)
        }
    static [Mail_Interface] BuildExample2()
        {
        $mail_object = "Pretend Local Group"
        return [Mail_Factory]::Build($mail_object)
        }
    static [Mail_Interface] Build($mail_object)
        {
        [Mail_Interface] $interface = [Mail_Interface]::new()
        $interface.config = [Mail_Config]::new($mail_object)
        $interface.retrieve = [Mail_Retrieve]::new($interface.config)
        $interface.search = [Mail_Search]::new($interface.config)

        [Mail_Retrieve_Members_Style] $members_style = switch ($mail_object)
            {
            ("Pretend 365 group") { [Mail_Retrieve_Members_365Group]::new($interface.config) }
            ("Pretend Local Group") { [Mail_Retrieve_Members_LocalGroup]::new($interface.config) }
            }
        # notice that we're setting a specific retreival strategy for "members" depending on object type
        $interface.config.members_style = $members_style

        return $interface
        }
    }

Class Mail_Interface
    {
    Mail_Interface(){}

    [Mail_Config] $config
    [Mail_Retrieve] $retrieve # This is a facade to unify the way we call different kinds of retreivals (directly from settings, derived from settings, shared based on type, or unique to a specific combination of settings)
    [Mail_Search] $search     # This is a facade for the same reasons as $retreive
    [bool] has_member($search) {return $this.search.has_member($search)}
    [object] members() {return @($this.retrieve.members())}
    }

Class Mail_Config
    {
    Mail_Config($mail_object) {$this.mail_object = $mail_object}

    [Mail_Retrieve_Members_Style] $members_style # set by factory
    [object] $mail_object
    }

Class Mail_Retrieve
    {
    Mail_Retrieve($config){$this.config = $config}

    [Mail_Config] $config
    [object] Members(){return $this.config.members_style.Members()}
    }

Class Mail_Retrieve_Members_Style
    {
    Mail_Retrieve_Members_Style($config){$this.config = $config}

    [Mail_Config] $config
    [object] Members(){throw("Syle not yet selected")}
    }

Class Mail_Retrieve_Members_365Group : Mail_Retrieve_Members_Style
    {
    Mail_Retrieve_Members_365Group($config) : base($config) {}

    # inherited: [Mail_Config] $config
    [object] Members(){return "member of 365 group"}
    }

Class Mail_Retrieve_Members_LocalGroup : Mail_Retrieve_Members_Style
    {
    Mail_Retrieve_Members_LocalGroup($config) : base($config) {}

    # inherited: [Mail_Config] $config
    [object] Members(){return "member of local group"} 
    }

Class Mail_Search
    {
    Mail_Search($config){$this.config = $config}

    [Mail_Config] $config
    [bool] has_member($search)
        {
        $members = $this.parent.retrieve.members() # !!! Problem exists here !!!
        if ($members -contains "$search") {return $true}
        else {return $false}
        }
    }

function TestExample1()
    {
    # from
    #   $a.search.has_member()
    # we're trying to access 
    #   $a.retrieve.Members()

    $a = [Mail_Factory]::BuildExample1()
    $member_a = $a.members()[0]
    if ($a.has_member($member_a))
        {"Success!"}
    else
        {"Failed, member match incorrect"}
    }


function TestExample2()
    {
    # from
    #   $b.search.has_member()
    # we're trying to access 
    #   $b.retrieve.Members()

    $b = [Mail_Factory]::BuildExample2()
    $member_b = $b.members()[0]
    $b.has_member($member_b)
    if ($b.has_member($member_b))
        {"Success!"}
    else
        {"Failed, member match incorrect"}
    }

$result_a = TestExample1
$result_b = TestExample2

$result_a
$result_b

这会吐出以下错误(我已用!!! Problem exists here !!! 标记了该行)

You cannot call a method on a null-valued expression.
At line:88 char:9
+         $members = $this.super.retrieve.members() # !!! Problem exist ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

我对这个项目有完全的控制权,如果我以错误的方式处理这个问题,我目前愿意完全重构为不同的方法。我很喜欢使用链式构造函数,特别是如果它们提高了可读性。 我一直在G4G 上探索设计模式,但我对它们的体验仍然是新手

【问题讨论】:

    标签: powershell class design-patterns siblings


    【解决方案1】:

    我最终用对父级 (Interface) 的引用替换了对 Config 的引用。虽然这打开了意外递归的可能性,但这似乎是唯一的方法(我还没有找到)

    我还扁平化了这些方法,删除了 .search.retrieve,因为现在不需要它们。

    Class Mail_Factory
        {
        static [Mail_Interface] BuildExample1()
            {
            $mail_object = "Pretend 365 group"
            return [Mail_Factory]::Build($mail_object)
            }
        static [Mail_Interface] BuildExample2()
            {
            $mail_object = "Pretend Local Group"
            return [Mail_Factory]::Build($mail_object)
            }
        static [Mail_Interface] Build($mail_object)
            {
            [Mail_Interface] $interface = [Mail_Interface]::new()
            $interface.config = [Mail_Config]::new($mail_object)
            #$interface.retrieve = [Mail_Retrieve]::new($interface)
            #$interface.search = [Mail_Search]::new($interface)
    
            [Mail_Members_Style] $members_style = switch ($mail_object)
                {
                ("Pretend 365 group") { [Mail_Members_365Group]::new($interface) }
                ("Pretend Local Group") { [Mail_Members_LocalGroup]::new($interface) }
                }
            # notice that we're setting a specific retreival strategy for "members" depending on object type
            $interface.members_style = $members_style
    
            $interface.has_member_style = [Mail_HasMember_Default]::new($interface)
    
            return $interface
            }
        }
    
    Class Mail_Interface
        {
        Mail_Interface(){}
    
        [Mail_Config] $config
    
        # set by factory #
        [Mail_HasMember_Style]$has_member_style 
        [Mail_Members_Style]$members_style
        # set by factory #
    
        [bool] HasMember($search) {return $this.has_member_style.HasMember($search)}
        [object] members() {return @($this.members_style.Members())}
        }
    
    Class Mail_Config
        {
        Mail_Config($mail_object) {$this.mail_object = $mail_object}
    
        # IRL we store a lot more in here
        [object] $mail_object
        }
    
    Class Mail_Members_Style
        {
        Mail_Members_Style($interface){$this.interface = $interface}
    
        [Mail_Interface] $interface
        [object] Members(){throw("Syle not yet selected")}
        }
    
    Class Mail_Members_365Group : Mail_Members_Style
        {
        Mail_Members_365Group($interface) : base($interface) {}
    
        # inherited: [Mail_Interface] $interface
        [object] Members(){return "member of 365 group"}
        }
    
    Class Mail_Members_LocalGroup : Mail_Members_Style
        {
        Mail_Members_LocalGroup($interface) : base($interface) {}
    
        # inherited: [Mail_Interface] $interface
        [object] Members(){return "member of local group"} 
        }
    
    Class Mail_HasMember_Style
        {
        Mail_HasMember_Style($interface){$this.interface = $interface}
    
        [Mail_Interface] $interface
        [bool] HasMember($search){throw("Syle not yet selected")}
        }
    
    Class Mail_HasMember_Default : Mail_HasMember_Style
        {
        Mail_HasMember_Default($interface) : base($interface) {}
    
        # inherited: [Mail_Interface] $interface
        [bool] HasMember($search)
            {
            $members = $this.interface.members()
            if ($members -contains "$search") {return $true}
            else {return $false}
            }
        }
    
    
    function TestExample1()
        {
        # from
        #   $a.has_member_style.has_member()
        # we're trying to access 
        #   $a.members_style.Members()
    
        $a = [Mail_Factory]::BuildExample1()
        $member_a = $a.members()[0]
        if ($a.HasMember($member_a))
            {"Success!"}
        else
            {"Failed, member match incorrect"}
        }
    
    
    function TestExample2()
        {
        # from
        #   $b.has_member_style.hasmember()
        # we're trying to access 
        #   $b.members_style.Members()
    
        $b = [Mail_Factory]::BuildExample2()
        $member_b = $b.members()[0]
        if ($b.HasMember($member_b))
            {"Success!"}
        else
            {"Failed, member match incorrect"}
        }
    
    
    function TestExample3()
        {
        # Verifying HasMember is actually checking stuff
    
        $b = [Mail_Factory]::BuildExample1()
        if ($b.HasMember("Not A Member"))
            {"Failed, HasMember is incorrectly returning true"}
        else
            {"Success!"}
        }
    
    $result_a = TestExample1
    $result_b = TestExample2
    $result_c = TestExample2
    
    $result_a
    $result_b
    $result_c
    

    以及测试结果

    Success!
    Success!
    Success!
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-21
      • 2011-05-01
      • 2019-08-04
      • 1970-01-01
      • 1970-01-01
      • 2016-03-16
      相关资源
      最近更新 更多