C# вопрос по ООП

132
24 декабря 2019, 09:20

Задумался как сделать логичнее (правильнее). Есть два класса: один ставит лайки в нём два метода лайки по тэгу и лайки по геолокации, второй подписывается в нём тоже два метода подписываться по тэгу и по геолокации.

Получается так, что у меня практически полностью дублируется один и тот же код в этих методах и классах. Разница в коде буквально в паре полей. Т.е. очень не удобно когда что-то меняешь, приходится менять всё тоже самое ещё в 3х методах.

Понимаю, что можно было бы сделать один класс с одним методом например: _work.Run(param1,param2,param3 ...); , но думаю это потом негативно отразится при глобальном расширении проекта. А я хочу сделать два класса, что логичнее на мой взгляд и потом проще для понимания. _like.Run(param1,param2,param3 ...) и _subscribe.Run(param1,param2,param3 ...) но при этом что бы код методов не дублировался в каждом классе.

UPD

Запуск из формы

 try
        {
            //обратите внимание на передачу токена отмены, и экземпл. прогресса

            Task.Factory.StartNew(() => {  //new thread
                if (radioButton1.Checked == true)
                {
                    if (radioButton3.Checked == true)
                    {
                        _like.Tags(tag, time_wait, limit, filters, cancelToken);
                    }
                    if (radioButton4.Checked == true)
                    {
                        _like.Geolocation(geonumber, time_wait, limit, filters, cancelToken);
                    }
                }
                if (radioButton6.Checked == true)
                {
                    if (radioButton3.Checked == true)
                    {
                        //подписка по тэгу
                        _subscribe.Tags(tag, time_wait, limit, filters, cancelToken);
                    }
                    if (radioButton4.Checked == true)
                    {
                        _subscribe.Geolocation(geonumber, time_wait, limit, filters, cancelToken);
                    }
                }
            });

        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Задача отменена.");
        }
        catch (Exception ex)
        {
            MessageBox.Show("В задаче произошла ошибка: " + ex, "Threads error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

класс Subscribe

 class Subscribe
{
    String filter_view_in_msg_box = "без фильтров";
    private IWebDriver _driver;
    private ControlFormProgramm _controlFormProgramm;
    public Subscribe(IWebDriver driver, ControlFormProgramm controlFormProgramm)
    {
        _driver = driver;
        _controlFormProgramm = controlFormProgramm;
    }
    public void Tags(string tag, int time_wait, int limit, string filters, CancellationToken cancelToken)
    {
        _driver.Navigate().GoToUrl("https://www.instagram.com/explore/tags/" + tag);
        _driver.FindElement(By.CssSelector("#react-root > section > main > article > div.EZdmt > div > div > div:nth-child(1) > div:nth-child(1)")).Click(); // открыть пост Самая первая запись в теге
        double dlimit = Convert.ToDouble(limit);

        for (int i = 1; i <= limit; i++)
        {
            if (Form1.stoped != 1)
            {
                try
                {
                    if (GetIsCheckedSubscribe() == false && Filters_word(filters) == true)
                    {
                        double di = Convert.ToDouble(i);

                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > header > div.o-MQd > div.PQo_0 > div.bY2yH > button")).Click(); //кнопка подпис. в открытом посте
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " подписались" + " (" + filter_view_in_msg_box + ")");
                        _controlFormProgramm.ProcessBarSetValue(Convert.ToInt32(di / dlimit * 100));
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(time_wait));
                    }
                    else
                    {
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " пропускаем, уже подписаны или фильтр");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        i = i - 1;
                        double di = Convert.ToDouble(i);
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                    }
                    if (i >= limit)
                    { //цикл выполнен
                        _controlFormProgramm.ProcessBarSetValue(100);
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        ControlFormProgramm.MsgLogBox.AddMsg("      Завершено.");
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        _controlFormProgramm.SetTextStateLabel("Завершено.");
                        _controlFormProgramm.Stop();
                    }
                }
                catch (Exception e)
                {
                    ControlFormProgramm.MsgLogBox.AddMsg("Error: " + e.Message);
                    i = i - 1;
                    _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                }
            }                //принудительная остановка
            else
            {
                i = limit;
                _controlFormProgramm.ProcessBarSetValue(100);
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                ControlFormProgramm.MsgLogBox.AddMsg("      Остановлено.");
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                _controlFormProgramm.SetTextStateLabel("Остановлено.");
                _controlFormProgramm.Stop();
            }
        }

    }
    public void Geolocation(string geonumber, int time_wait, int limit, string filters, CancellationToken cancelToken)
    {
        _driver.Navigate().GoToUrl("https://www.instagram.com/explore/locations/" + geonumber);
        _driver.FindElement(By.CssSelector("#react-root > section > main > article > div.EZdmt > div > div > div:nth-child(1) > div:nth-child(1)")).Click(); // открыть пост Самая первая запись в теге
        double dlimit = Convert.ToDouble(limit);

        for (int i = 1; i <= limit; i++)
        {
            if (Form1.stoped != 1)
            {
                try
                {
                    if (GetIsCheckedSubscribe() == false && Filters_word(filters) == true)
                    {
                        double di = Convert.ToDouble(i);

                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > header > div.o-MQd > div.PQo_0 > div.bY2yH > button")).Click(); //кнопка подпис. в открытом посте
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " подписались" + " (" + filter_view_in_msg_box + ")");
                        _controlFormProgramm.ProcessBarSetValue(Convert.ToInt32(di / dlimit * 100));
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(time_wait));
                    }
                    else
                    {
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " пропускаем, уже подписаны или фильтр");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        i = i - 1;
                        double di = Convert.ToDouble(i);
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                    }
                    if (i >= limit)
                    { //цикл выполнен
                        _controlFormProgramm.ProcessBarSetValue(100);
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        ControlFormProgramm.MsgLogBox.AddMsg("      Завершено.");
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        _controlFormProgramm.SetTextStateLabel("Завершено.");
                        _controlFormProgramm.Stop();
                    }
                }
                catch (Exception e)
                {
                    ControlFormProgramm.MsgLogBox.AddMsg("Error: " + e.Message);
                    i = i - 1;
                    _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                }
            }                //принудительная остановка
            else
            {
                i = limit;
                _controlFormProgramm.ProcessBarSetValue(100);
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                ControlFormProgramm.MsgLogBox.AddMsg("      Остановлено.");
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                _controlFormProgramm.SetTextStateLabel("Остановлено.");
                _controlFormProgramm.Stop();
            }
        }

    }
    public Boolean GetIsCheckedSubscribe()
    {
        String getAttribut;
        Boolean ischeck;
        getAttribut = _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > header > div.o-MQd > div.PQo_0 > div.bY2yH > button")).GetAttribute("class");
       Console.WriteLine(getAttribut); 
       if (getAttribut == "oW_lN _0mzm- sqdOP yWX7d    _8A5w5    ")
        {
            ischeck = true;
        }
        else
        {
            ischeck = false;
        }
        return ischeck;
    }
    public Boolean Filters_word(string _filter_words)
    {
        bool result;
        String bodyText=" ";
        if (Form1.MyGlavForm.checkBox1.Checked == true)
        {
            String filters = _filter_words;
            try
            {
                IWebElement body = _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > div.EtaWk"));
                bodyText = body.Text; //текст поста #react-root > section > main > div > div > article > div.eo2As > div.KlCQn.EtaWk > ul > li > div > div > div > span
            }
            catch (Exception ) { filter_view_in_msg_box = "null"; return result = true; }
                                         // Console.WriteLine(bodyText);
            string[] words = filters.Split(',');
            foreach (string word in words)
            {
                result = Regex.IsMatch(bodyText, @"\b" + word + @"\b", RegexOptions.IgnoreCase);
                if (result == true)
                {
                    filter_view_in_msg_box = word;
                    return result;
                }
            }
            return result = false;
        }
        else { return result = true; }
    }
}

Класс лайкер

 class Like
{
    String filter_view_in_msg_box = "без фильтров";
    private IWebDriver _driver;
            private ControlFormProgramm _controlFormProgramm;
    public Like(IWebDriver driver, ControlFormProgramm controlFormProgramm) {
        _driver = driver;
        _controlFormProgramm = controlFormProgramm;
    }

    public void Tags(string tag, int time_wait, int limit, string filters, CancellationToken cancelToken) {
        _driver.Navigate().GoToUrl("https://www.instagram.com/explore/tags/"+tag);
        _driver.FindElement(By.CssSelector("#react-root > section > main > article > div.EZdmt > div > div > div:nth-child(1) > div:nth-child(1)")).Click(); // открыть пост Самая первая запись в теге
        double dlimit = Convert.ToDouble(limit);

        for (int i = 1; i <= limit; i++)
        {
            if (Form1.stoped != 1)
            {
                try
                {
                    if (GetIsCheckedLike() == false && Filters_word(filters)==true)
                    {
                        double di = Convert.ToDouble(i);

                         _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > section.ltpMr.Slqrh > span.fr66n > button")).Click(); //кнопка лайка в открытом посте
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " лайк добавлен" +" ("+filter_view_in_msg_box+")");
                        _controlFormProgramm.ProcessBarSetValue(Convert.ToInt32(di / dlimit * 100));
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(time_wait));
                    }
                    else
                    {
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " пропускаем, уже лайкали или фильтр");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        i = i - 1;
                        double di = Convert.ToDouble(i);
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                    }
                    if (i >= limit)
                    { //цикл выполнен
                        _controlFormProgramm.ProcessBarSetValue(100);
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        ControlFormProgramm.MsgLogBox.AddMsg("      Завершено.");
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        _controlFormProgramm.SetTextStateLabel("Завершено.");
                        _controlFormProgramm.Stop();
                    }
                }
                catch (Exception e) {
                    ControlFormProgramm.MsgLogBox.AddMsg("Error: " + e.Message);
                    i = i - 1;
                    _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                }
            }                //принудительная остановка
            else {
                i = limit;
                _controlFormProgramm.ProcessBarSetValue(100);
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                ControlFormProgramm.MsgLogBox.AddMsg("      Остановлено.");
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                _controlFormProgramm.SetTextStateLabel("Остановлено.");
                _controlFormProgramm.Stop();
            }
        }

    }
    public void Geolocation(string geonumber, int time_wait, int limit, string filters, CancellationToken cancelToken)
    {
        _driver.Navigate().GoToUrl("https://www.instagram.com/explore/locations/" + geonumber);
        _driver.FindElement(By.CssSelector("#react-root > section > main > article > div.EZdmt > div > div > div:nth-child(1) > div:nth-child(1)")).Click(); // открыть пост Самая первая запись в теге
        double dlimit = Convert.ToDouble(limit);

        for (int i = 1; i <= limit; i++)
        {
            if (Form1.stoped != 1)
            {
                try
                {
                    if (GetIsCheckedLike() == false && Filters_word(filters) == true)
                    {
                        double di = Convert.ToDouble(i);

                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > section.ltpMr.Slqrh > span.fr66n > button")).Click(); //кнопка лайка в открытом посте
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " лайк добавлен" + " (" + filter_view_in_msg_box + ")");
                        _controlFormProgramm.ProcessBarSetValue(Convert.ToInt32(di / dlimit * 100));
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(time_wait));
                    }
                    else
                    {
                        cancelToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
                        ControlFormProgramm.MsgLogBox.AddMsg(_driver.Url + " пропускаем, уже лайкали или фильтр");
                        _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                        i = i - 1;
                        double di = Convert.ToDouble(i);
                        _controlFormProgramm.SetTextStateLabel(limit + " / " + i + " (" + Convert.ToInt32(di / dlimit * 100) + " %)");
                    }
                    if (i >= limit)
                    { //цикл выполнен
                        _controlFormProgramm.ProcessBarSetValue(100);
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        ControlFormProgramm.MsgLogBox.AddMsg("      Завершено.");
                        ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                        _controlFormProgramm.SetTextStateLabel("Завершено.");
                        _controlFormProgramm.Stop();
                    }
                }
                catch (Exception e)
                {
                    ControlFormProgramm.MsgLogBox.AddMsg("Error: " + e.Message);
                    i = i - 1;
                    _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow")).Click(); //следующий пост
                }
            }                //принудительная остановка
            else
            {
                i = limit;
                _controlFormProgramm.ProcessBarSetValue(100);
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                ControlFormProgramm.MsgLogBox.AddMsg("      Остановлено.");
                ControlFormProgramm.MsgLogBox.AddMsg("=======================");
                _controlFormProgramm.SetTextStateLabel("Остановлено.");
                _controlFormProgramm.Stop();
            }
        }

    }
    public Boolean GetIsCheckedLike()
    {
        String getAttribut;
        Boolean ischeck;
        getAttribut = _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > section.ltpMr.Slqrh > span.fr66n > button > span")).GetAttribute("class");
        Console.WriteLine(getAttribut); 
        if (getAttribut == "glyphsSpriteHeart__outline__24__grey_9 u-__7")
        {
            ischeck = false;
        }
        else {
            ischeck = true;
        }
        return ischeck;
    }
    public Boolean Filters_word(string _filter_words)
    {
        bool result;
        String bodyText = " ";
        if (Form1.MyGlavForm.checkBox1.Checked == true)
        {
            String filters = _filter_words;
            try
            {
                IWebElement body = _driver.FindElement(By.CssSelector("body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > div.EtaWk"));
                bodyText = body.Text; //текст поста #react-root > section > main > div > div > article > div.eo2As > div.KlCQn.EtaWk > ul > li > div > div > div > span
            }
            catch (Exception) { filter_view_in_msg_box = "null"; return result = true;  }
            // Console.WriteLine(bodyText);
            string[] words = filters.Split(',');
            foreach (string word in words)
            {
                result = Regex.IsMatch(bodyText, @"\b" + word + @"\b", RegexOptions.IgnoreCase);
                if (result == true)
                {
                    filter_view_in_msg_box = word;
                    return result;

                }
            }
            return result = false;
        }
        else { return result = true; }
    }



}

Т.е. получается разница только в CSS селекторах, а код идентичный.

Я сделал статический класс с константами

    public static class ConstantsApp
{
    public const String app_version = "1.2";
    public const String app_author = "Martinov A.";
    public const String url_tags = "https://www.instagram.com/explore/tags/"; //
    public const String url_geolocation = "https://www.instagram.com/explore/locations/"; //
    public const String html_mark_open_first_post = "#react-root > section > main > article > div.EZdmt > div > div > div:nth-child(1) > div:nth-child(1)"; // открыть пост Самая первая запись в теге
    public const String html_mark_panel_textbox = "body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > div.EtaWk"; // панель, где находится текст поста
    public const String html_mark_button_next = "body > div._2dDPU.vCf6V > div.EfHg9 > div > div > a.HBoOv.coreSpriteRightPaginationArrow"; // кнопка следующий пост (так же, можно использовать событие клавы)
    public const String html_mark_button_like = "body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > section.ltpMr.Slqrh > span.fr66n > button"; //кнопка лайка в открытом посте
    public const String html_mark_get_check_like = "body > div._2dDPU.vCf6V > div.zZYga > div > article > div.eo2As > section.ltpMr.Slqrh > span.fr66n > button > span"; // проверка стоит ли лайк
    public const String html_mark_button_subscribe = "body > div._2dDPU.vCf6V > div.zZYga > div > article > header > div.o-MQd > div.PQo_0 > div.bY2yH > button"; // //кнопка подпис. в открытом посте
    public const String html_mark_get_check_subscribe = "body > div._2dDPU.vCf6V > div.zZYga > div > article > header > div.o-MQd > div.PQo_0 > div.bY2yH > button"; // проверка, подписаны ли
    public const String html_mark_name_attribut_grey_like = "glyphsSpriteHeart__outline__24__grey_9 u-__7"; // имя атрибута когда лайк не стоит (серое сердечко)
    public const String html_mark_name_attribut_active_sucbscribe = "oW_lN _0mzm- sqdOP yWX7d    _8A5w5    "; // имя атрибута когда не подписаны
}

И хочу что бы у меня был только один метод, а не 4. Буду просто селекторы разные передавать в метод из статического класса с константами. Но при этом хотелось бы, что бы было два класса. Другими словами два класса и только один общий метод у них ну или что-то вроде этого))

P.S. за код не ругайте, я только учусь. =)

Answer 1

я с АПИ твиттера не работал, потому покажу не боевой код, а скорее небольшое упражнение в ООП.

Первым делом нам нужен класс твита. Поля твита я взял из головы, но сделал его неизменяемым классом, так как, насколько я помню, твит поменять нельзя

public class TweetItem
{
    public string Id { get; }
    public string Text { get; }
    public string Author { get; }
    public TweetItem(string id, string text, string author)
    {
        this.Id = id;
        this.Text = text;
        this.Author = author;
    }
}

Дальше, нам нужна возможность искать твиты. Так как поиск твитов может быть по разным критериям, я решил выделить интерфейс для поиска

public interface ITweetFilter
{
    IEnumerable<TweetItem> GetTweets();
}

Далее, какая-то реализация классов поиска

public class TweetFilterByTag : ITweetFilter
{
    private string _tag;
    public TweetFilterByTag(string tag)
    {
        _tag = tag;
    }
    public IEnumerable<TweetItem> GetTweets()
    {
        throw new NotImplementedException();
    }
}

public class TweetFilterByGeo : ITweetFilter
{
    private string _geo;
    public TweetFilterByGeo(string geo)
    {
        _geo = geo;
    }
    public IEnumerable<TweetItem> GetTweets()
    {
        throw new NotImplementedException();
    }
}

Поскольку поиск можно комбинировать, я решил также накатать композитный поиск.

public class TweetFilterComposite : ITweetFilter
{
    private ITweetFilter[] _filters;
    public TweetFilterComposite(params ITweetFilter[] filters)
    {
        _filters = filters;
    }
    public IEnumerable<TweetItem> GetTweets()
    {
        var foundTweets = new HashSet<string>();
        foreach (var filter in _filters)
            foreach (var tweet in filter.GetTweets())
                if (foundTweets.Add(tweet.Id))
                    yield return tweet;
    }
}

Далее, нам нужны классы, один для постановки лайков, второй - для подписки.

public class TweetLiker
{
    public void SetLike(TweetItem tweet)
    {
        throw new NotImplementedException();
    }
}
public class TweetSubscriber
{
    public void Subscribe(TweetItem tweet)
    {
        throw new NotImplementedException();
    }
}

Осталось соединить всё вместе в нашей логике

public class MyLogic
{   
    private TweetLiker _liker;
    private TweetSubscriber _subscriber;
    public MyLogic(TweetLiker liker, TweetSubscriber subscriber)
    {       
        _liker = liker;
        _subscriber = subscriber;
    }
    public void SetLike(ITweetFilter filter)
    {
        foreach(var tweet in filter.GetTweets()
        _liker.SetLike(tweet);
    }
    public void Subscribe(ITweetFilter filter)
    {
        foreach (var tweet in filter.GetTweets()
        _subscriber.Subscribe(tweet);
    }
}

Как всем этим пользоваться: создаем нужные фильтры, создаем логику и вперед.

var filterByTag = new TweetFilterByTag("MyTag");
var filterByGeo = new TweetFilterByGeo("MyCity");
var compositeFilter = new TweetFilterComposite(filterByTag, filterByGeo);
var myLogic = new MyLogic(new TweetLiker(), new TweetSubscriber());
myLogic.SetLike(compositeFilter);
myLogic.Subscribe(compositeFilter);
READ ALSO
Как добавить MouseEventHandler к PictureBox?

Как добавить MouseEventHandler к PictureBox?

Собственно есть такой код, где в цикле создаются PictureBox

148
Обобщения оператор where

Обобщения оператор where

Подскажите как загуглить что бы почитать о операторе/ключевом слове where в такой конструкции например:

152
Как подсчитать сумму ячеек в строке dataGridView C#

Как подсчитать сумму ячеек в строке dataGridView C#

Нужно подсчитать сумму ячеек для каждой строки таблицы dataGridViewКол-во строк и столбцов заранее не известно

133
Вызов метода из другого скрипта C#

Вызов метода из другого скрипта C#

Есть 2 класса( 2 разных скрипта)Первый:

139