Проблем с капсулирането с делегати?

Чудя се защо това работи?

Например имам някакъв клас изпълнител, който изглежда така:

public class Executor
{
    public void Execute(Action action)
    {
        action();
    }
}

Сега имам някакъв клас, който трябва да бъде изпълнен, който изглежда така:

public class NeedToBeExecuted
{
    public void Invoke()
    {
        Executor executor = new Executor();
        executor.Execute(DoSomething);
    }

    private void DoSomething()
    {
        // do stuff private
    }
}

Въпросът ми е защо това е работа, като предавам частен метод на друг клас?

Това не е ли проблем с капсулирането?


person IamStalker    schedule 23.03.2013    source източник
comment
имаме ли проблем с капсулирането, когато дефинираме метод, който се изпълнява като ThreadRunner?   -  person bas    schedule 23.03.2013
comment
правете неща частни ... добре   -  person Ken Kin    schedule 23.03.2013
comment
Вие предавате метода като Action. Въпреки че не е съвсем същото, помислете дали предавате private масив или някакъв клас като параметър на друг метод. Няма нищо лошо в това. DoSomething също ще има достъп до личните променливи от NeedToBeExecuted, които използва поради затваряния.   -  person Oscar Mederos    schedule 23.03.2013


Отговори (6)


Не, това не е нарушение на капсулирането.

Първо: самият клас е нещото, което реши да раздаде делегат на един от своите частни методи! Класът има достъп до своите частни методи и ако избере да предаде препратка към такъв, за да кодира извън домейна за достъпност на този метод, това е напълно в неговите права.

Второ: домейнът за достъпност на даден метод не ограничава къде методът може да бъде извикан. Ограничава къде методът може да се търси по име. Клас Executor никога не използва името DoSomething за извикване на частния метод. Използва името action.

person Eric Lippert    schedule 23.03.2013

Нека да опитам тогава.

Не, нямаме проблем с капсулирането. Вие дефинирате клас, който отговаря за Execute нещо. Executer не знае нищо за това какво всъщност се изпълнява. Той знае само, че трябва да изпълни DoSomething. Какво прави DoSomething е посочено в класа, който отговаря за изпълнението на нещо.

Както коментирах, това е доста подобно на това, което бихте направили с Thread. Вие дефинирате метод, който трябва да бъде изпълнен на различна нишка. Ето защо класът има нишка и дефинира кой метод трябва да се изпълнява в тази нишка. Нишката все още не знае нищо за класа, в който играе роля.

Релациите са Клас => Нишка. Не обратното.

Във вашия пример отношенията са NeedToBeExecuted => Executer. Не обратното.

Концепцията може да е сложна за ума ви, но не правите нищо лошо.

person bas    schedule 23.03.2013

Това е толкова голям проблем с капсулирането, колкото и използването на отражение. Възможно е да получите достъп до частни методи и променливи от външен клас доста лесно, като използвате отражение. Не трябва да смятате това непременно за лошо, тъй като то просто прави езика много по-мощен.

Класът е този, който така или иначе се отказва от метода на делегиране, така че по някакъв начин класът позволява поведението, така че изобщо не би трябвало да е проблем с капсулирането.

person Daniel Imms    schedule 23.03.2013
comment
но изпълнителят няма достъп до частни членове на класа NeedToBeExecuted. Получава само „функционален указател“. Тук капсулирането не е проблем. Не че „може“ да е проблем с отражението. - person bas; 23.03.2013

Помислете за това: NeedToBeExecuted не излага метода DoSomething() по такъв начин, че да може да бъде извикан произволно - вместо това го предава като делегат на друга функция. Може също толкова лесно да премине () => DoSomething() - резултатът е същият. В крайна сметка модификаторите за достъп са там, за да предотвратят различен клас, използващ вашия метод, вие все още сте свободни да го използвате, както желаете. Ако решите да го предадете на друг клас, това е валидна употреба.

person AlexFoxGill    schedule 23.03.2013

Капсулирането означава да можете да промените вътрешността на някой модул, без да променяте външния код. Тук все още е така. Така че не е настъпило нарушение на капсулирането.

person usr    schedule 23.03.2013
comment
Намирам за интересно как различните хора гледат по различен начин на целта на капсулирането. Въпреки че капсулирането осигурява застраховка срещу зависимост от детайли на механизъм, който може да се промени, аз виждам основната цел на капсулирането като даване на възможността на програмиста да поддържа инвариант, на който друг код може да разчита на свой ред. - person Eric Lippert; 23.03.2013

Подавате DoSomething в метода Invoke() и той е достъпен в контекста. Няма проблем с капсулирането.

Въпреки това, както виждате коментара в кода, DoSomething не е достъпен в EitherNeedToBeExecuted.

public class Executor {
    public void Execute(Action action) {
        action();
    }
}

public class NeedToBeExecuted {
    public virtual void Invoke() {
        Executor executor=new Executor();
        executor.Execute(this.DoSomething);
    }

    private void DoSomething() {
        Console.WriteLine("I'M DOING IT MYSELF!!");
    }

    protected Executor m_Executor=new Executor();
}

public class EitherNeedToBeExecuted: NeedToBeExecuted {
    public override void Invoke() {
        // 'NeedToBeExecuted.DoSomething()' is inaccessible due to its protection level
        m_Executor.Execute(base.DoSomething);
    }
}
person Ken Kin    schedule 23.03.2013