C# Паттерны Проектирования - Пример

Подробнее о теории паттернов здесь

Итак - пусть у нас есть программа:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace CheckersPatternGame
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

мы будем играть в шашки
Вот код адаптера - адоптируем данные в команду:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace CheckersPatternGame
{
    public class Adapter : myCheckersCommand1
    {
        
        public TextBox tx1, ty1, tx2, ty2; // текст в этих вот друзьях мы будем адаптировать в команду.
        public Adapter(Facade Adrr, TextBox tx12,TextBox ty12,TextBox tx22,TextBox ty22) 
        {
            this.Addressee = Adrr;
            tx1 = tx12;
            ty1 = ty12;
            tx2 = tx22;
            ty2 = ty22;
            this.Update();
        }
        public void Update() // проверка и запоминание координат.
        {  int a;
            if (Int32.TryParse(tx1.Text, out a))
            { if  ((a>=1)&&(a<=8))
                this.x1 = a; 
            }
            else
                throw new UnexpectedTypeExeption("В качестве координат указывайте целые числа от одного до восьми!");
            if (Int32.TryParse(ty1.Text, out a))
            {
                if ((a >= 1) && (a <= 8))
                    this.y1 = a;
            }
            else
                throw new UnexpectedTypeExeption("В качестве координат указывайте целые числа от одного до восьми!");
            if (Int32.TryParse(tx2.Text, out a))
            {
                if ((a >= 1) && (a <= 8))
                    this.x2 = a;
            }
            else
                throw new UnexpectedTypeExeption("В качестве координат указывайте целые числа от одного до восьми!");
            if (Int32.TryParse(ty2.Text, out a))
            {
                if ((a >= 1) && (a <= 8))
                    this.y2 = a;
            }
            else
                throw new UnexpectedTypeExeption("В качестве координат указывайте целые числа от одного до восьми!");
            
        }
    
    }
    //---------далее немного исклбчений-------------------
    public class  UnexpectedTypeExeption : ChackersExeption // базовый класс исключений для нашей коллекции
    {
        private const string SelfMessage = " Unexpected Type -> ";
        public UnexpectedTypeExeption() : base(String.Format("{0}", SelfMessage)) { }
        public UnexpectedTypeExeption(string s) : base(String.Format("{0}{1}", SelfMessage, s)) { } // формируем Message-свойство -сообщение.
    }
}

а вот это фасад - он будет хранить состояние игрового поля:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CheckersPatternGame
{
    public abstract class Facade
    {
        public abstract void Make_a_Move(int x1,int x2,int y1,int y2); // даже для шахмат такой вот обобщённый формат хода подойдёт.
    }
    public class ChakersFacade1 : Facade
    { // ?????? почему в конструкторе нельзя void в данном случае ?
        public Shashka[] FigMass; // Фасад будет хранить два массива фигур - одщий для обоих игров, те фигуру, что будут иметь индекс больше 12-ого  - это "чёрные" Наблюдатель нарисует их на форме сверху.
        public  ChakersFacade1() // в этом конструкторе мы займёмся "подготовкой" игры - "расставим " фигуры , определим их цвет - это "программа минимум"
        {
            int x = 1, y = 1; // начальные координаты для расстановки.
            bool b=false; // проверка чётности номера строки клеток (не помню функцию , позволяющую определить отстаток от деления в си шарп) перва я строка - чётная.
            this.FigMass= new Shashka[24]; // выделяем память для массива фигур.
           for (int i=0;i<=23;i++)  // инициаллизируем каждую фигуру.
           { // в цикле мы определяем цвет фигуры и её координаты ,причём перва яполучает координаты [1;1]
               FigMass[i] = new ProstoShashka();
               FigMass[i].X = x; // запоминаем координаты.
               FigMass[i].Y = y;
               if (i <= 11) FigMass[i].isWhite = true;  // то есть если это белая фигура. (чёрной фигура является по-умолчанию)
               // далее ряд опереций, связанных с вычислением очередной координаты
               if ((x == 7) && (y == 3)) // выполнение этого условия будет означать, что все белые уже расставлены.
               {
                   y = 6; x = 2; b = true; // определяем координаты первой чёрной фигуры.
               }
               else  // если все белые ещё не расставлены или раставлены более одной итерации цикла назад.
               {
                   if ((x != 7) && (x != 8)) x = x + 2; // расставляем через клетку потому и увеличиваем  икс на два.
                   else
                   {
                       y = y + 1; // переходим на следующую строка , если заполнена предыдущая.
                       if (b == false) b = true; else b = false; // меняем чётность/нечётность.
                       if (b == false) x = 1; else x = 2; // определяем начальную координату клетки на очередной сторе в зависимости от чётности/нечётности последней.

                   }
               }
           }

           this.UpdateObservers();// предлагаем наблюдателям обновиться  - показать все шашки на исходных позициях.
            //  на этом расстановка и покраска фигур завершается. теперь пишем обработчик хода.
        }
        Shashka ActiveFig; // фигура, которой собираются ходить, та, которая выбрана для хода.
        
        public override void Make_a_Move(int x1, int y1, int x2, int y2) // обработчик хода.
        {
            Shashka[] figuresOnThePathMass = null; // фигуры, которые находятся на траектории хода.
            Cell[] curcellmass; // координаты траектории передаваемого хода  -получаем этот массив от фигуры.
            Cell[] cellmassWithoutActiveFig = null; // массив координат без тех , на которых фигура итак размещена  -  и без последней, которая должна быть свободной для хода.
            this.ActiveFig = this.GetSpecFigure(x1, y1);
            if (ActiveFig != null)  
            { 
                curcellmass = ActiveFig.CanMoveTo(x2,y2);
                if (curcellmass != null) // если фигура теоретически может так походить ,то начинаем изучать ситуацию на поле
                { /* далее мы описываем фасад исходя из тех принципов, что любая шашка - как дамка так и не дамка должна за один ход либо не переступить 
                   * ни одной шашки либо переступить только одну - при элементарном ударе, при этом клетка с конечным адресом должна быть свободой.
                   * вот и вся логика*/
                    if (IsCellFree(x2, y2)) // проверяем доступность конечной координаты. - то , что клетка свободна.
                    {
                        if  (curcellmass.Length == 1) throw new NoPathExeption("шашка уже итак стоит на этой клетке.");
                        else if (curcellmass.Length == 2) // если это просто ход на соседнюю по-диагонали , тогда "ходим"
                        {
                            ActiveFig.X = x2;
                            ActiveFig.Y = y2;
                        }
                        else
                        {
                            // далее выделяем из массива координат те , которые не являются начальными и конечными.
                            cellmassWithoutActiveFig = new Cell[curcellmass.Length - 2]; //
                            int i=0;
                            foreach (Cell c in curcellmass) // переписываем интересующие нас координаты в новый массив.
                            {
                                cellmassWithoutActiveFig[i] = curcellmass[i + 1];
                            }
                        //    for (int i = 1; i <= curcellmass.Length - 1; i++)
                        //    {
                        //        cellmassWithoutActiveFig[i - 1] = curcellmass[i]; // переписываем интересующие нас координаты в новый массив.
                          //  }
                            figuresOnThePathMass = GetFigMassOnThePath(cellmassWithoutActiveFig); // получаем фигуры на интересующем нас отрезке траектории.
                            if (figuresOnThePathMass != null)
                            {
                                if ((figuresOnThePathMass.Length == 1) && (this.ActiveFig.isWhite != figuresOnThePathMass[0].isWhite)) // если на пути только одна шашка противника.
                                {
                                    figuresOnThePathMass[0].IsKilled = true; // шашка противника побита.
                                    ActiveFig.X = x2;
                                    ActiveFig.Y = y2;
                                }
                                else if ((figuresOnThePathMass.Length == 1) && (this.ActiveFig.isWhite == figuresOnThePathMass[0].isWhite))
                                    throw new CantBeatFriendlyUnitExeption(" своих не бьют!");
                                else if (figuresOnThePathMass.Length > 1)
                                    throw new CantMakeSoMoreSacrificesExeption("Не всё сразу. Бейте по-одной.");

                            }

 
                        }

                    }
 
                }
              
            }
            this.UpdateObservers();// предлагаем наблюдателям обновиться.

        } // здесь обработка хода заканчивается - можно приступать к системе визуализации   - Наблюдателю и Адаптеру, если говорить о паттернах.
        
        public bool IsCellFree(int x, int y) // вспомогательная функция, проверяет - является ли указнная ячейка свободной.
        {
            bool b = true;// ответ функции.
            foreach (Shashka curfigure in FigMass )
            {
                if ((curfigure.X == x) && (curfigure.Y == y)) b = false; // вот и вся проверка.
            }
            return b;
        }
        public Shashka GetSpecFigure(int x, int y) //  вспомогательная функция возвращает фигуру , если такова я имеется на пересечении указанных координат.
        {
            Shashka shash = null;
            foreach (Shashka curfigure in this.FigMass)
            {
                if ((curfigure.X == x) && (curfigure.Y == y)) { shash = curfigure; break; } // вот и вся проверка.
            }
            return shash;

        }
        public Shashka[] GetFigMassOnThePath(Cell[] cellmass) // возвращает массив фигур, размещённых намножестве переданных клеток.
        {
            Shashka[] rez= new Shashka[0] ; // function result
            foreach (Shashka shash in this.FigMass) // перебираем фигуры
            {
                foreach (Cell cell in cellmass) // перебираем клетки траектории.
                {
                    if ((shash.X == cell.x) && (shash.Y == cell.y))
                    {
                        Array.Resize(ref rez, rez.Length + 1);
                        rez[rez.Length - 1] = shash;
                    }
                }
            }
            
            return rez;
        }
        private Observer[] Observers = new Observer[0];
        public void UpdateObservers()
        {
            foreach (Observer v in Observers)
            {
                v.UpdateView();
            }
        }
        public void AddObserver(Observer nv )
        {
            Array.Resize(ref this.Observers, this.Observers.Length + 1);
            this.Observers[this.Observers.Length-1] = nv; // добавляем ещё одного наблюдателя, реализующего интерфейс, опять же, наблюдателя.
        }
    }
    //--------далее набор всевозможных исключительных ситуаций - чтобы играть было веселее.)))--------------
    public class ChackersExeption : ApplicationException // базовый класс исключений для нашей коллекции
    {
        private const string ChackersEceptionMessage = "(!) Problem with the checkers game-> ";
        public ChackersExeption() : base(String.Format("{0}", ChackersEceptionMessage)) { }
        public ChackersExeption(string s) : base(String.Format("{0}{1}", ChackersEceptionMessage, s)) { } // формируем Message-свойство -сообщение.
    }
    public class NoPathExeption :ChackersExeption // базовый класс исключений для нашей коллекции
    {
        private const string SelfMessage = " path doesn't exist -> ";
        public NoPathExeption() : base(String.Format("{0}", SelfMessage)) { }
        public NoPathExeption(string s) : base(String.Format("{0}{1}", SelfMessage, s)) { } // формируем Message-свойство -сообщение.
    }
    public class CantMoveExeption : ChackersExeption // базовый класс исключений для нашей коллекции
    {
        private const string SelfMessage = " can't move -> ";
        public CantMoveExeption() : base(String.Format("{0}", SelfMessage)) { }
        public CantMoveExeption(string s) : base(String.Format("{0}{1}", SelfMessage, s)) { } // формируем Message-свойство -сообщение.
    }
    public class CantBeatFriendlyUnitExeption : ChackersExeption // базовый класс исключений для нашей коллекции
    {
        private const string SelfMessage = "It's  friendly shachka -you can't beat it-> ";
        public CantBeatFriendlyUnitExeption() : base(String.Format("{0}", SelfMessage)) { }
        public CantBeatFriendlyUnitExeption(string s) : base(String.Format("{0}{1}", SelfMessage, s)) { } // формируем Message-свойство -сообщение.
    }
    public class CantMakeSoMoreSacrificesExeption : ChackersExeption // базовый класс исключений для нашей коллекции
    {
        private const string SelfMessage = " can't beat so more units at one motion -> ";
        public CantMakeSoMoreSacrificesExeption() : base(String.Format("{0}", SelfMessage)) { }
        public CantMakeSoMoreSacrificesExeption(string s) : base(String.Format("{0}{1}", SelfMessage, s)) { } // формируем Message-свойство -сообщение.
    }
 
}


а вот код для фигуры - он сделает её независимой от игрового мира:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
/*шашки паттерны ооп*/
namespace CheckersPatternGame
    // лучше всег начать с описания класса Фигура.
// начинаем с фигуры  -главного юнита нашей игры .
{ /* 1)Волевым решением создатель этого далеко не самого совершенного кода постоновляет -
     мы будем узнавать возможность сделать ход у фигуры  ,но так как фигура ничего не должна знать о фасаде по-идее (это сделает её универсальной)
     то фигура будет просто отвечать типа "в общем случае со своей клетки я могу походить на предлагаемую клетку" или же "не могу",
   * в свою очередь Фасад, который, как ,опять же - предполагает автор данного кода  ,"знает" о фигурах , которыми он двигает, и может перемещать их, или 
   * же убирать с поля "побитые"  - "спрашивает" у фигуры - может ли она чисто теоретически достигнуть указанной клетки,
   * если может   - он проводит ряд собственных проверок и действий, связанных с расположением наигровом уже других фигур
   * и если проверки закачиваются успехом(всё соответствует правилам) игры фасад производит соответствующие ходу преобразования игрового поля.
   * Вот, собственно, и всё задумка, отностильно взаимодействия фигуры и фасада  - 
   * фасад спрашивает может ли фигура чисто теоретически 
   * (то есть , не учитывая, например того, что на данной клетке уже кто-то стоит) совершить элементарный ход (или 
   * элементарный удар) на эту позицию (например фасад шашек, который мы здесь реализуем
   * спросит просто возможность однократного удара или хода  - сам при этом проверив -есть ли кого бить в случае удара и не занята ли клекта которыю 
   * предполагается занять после хода/удара) то есть - ключевой момент частого решения проектирования здесь состоит именно в проверке
   * элементарных хода и удара у фигуры  - все остальные решения же будет принимать фасад - причём некая в определённым смысле, "частная" его версия, 
   * заточенная для работы именно с данным типом фигур в данной игре (шашки и дамки в шашках, различные фигуры в шахматах, лиса и четыре гуся а игре
   * "Лиса и гуси")
   * 2)   В ходе дополнительных размышлений автор приходит к выводу о том, что фигура должна возврашать не только ответ о способности
   * совершить ход но и МАССИВ КООРДИНАТ ПРИНАДЛЕЖАЩИХ ЕЁ ТРАЕКТОРИИ ДАННОГО ХОДА. это сделает фигуру полностью самостоятельной , а фасад
   * в свою очередь уже будет отвечать только за взаимодействие фигур на поле.
   *
   */
    public abstract class Figure // это класс фигуры для "движка" игры, то есть Фасада. ????? - почему здесь нужен паблик перед классом  - иначе появится Error	1	Inconsistent accessibility: base class 'CheckersPatternGame.Figure' is less accessible than class 'CheckersPatternGame.Shashka'	
    {
        private int XFiled, YFiled; //целочисленные координаты фигуры на поле.
        public int X // 
        {
            get { return this.XFiled; }
            set { this.XFiled = value; }
        }
        public int Y // 
        {
            get { return this.YFiled; }
            set { this.YFiled = value; }
        }
        public bool IsKilled = false; // при инициализации фигура жива.
        public string Name; //  наименование фигуры (это поле инициализировать в игре не обязательно.)  - может пригодится  например, при выводе ошибки
        public abstract Cell[] CanMoveTo(int x2, int y2); // этот метод размышляет над тем  - можно ли походить или нет - если может - возвращает "true", иначе "false"
    }
    //------------------------------
    // далее -классы для реализации паттерна Состояния фигуры.
    public abstract class Shashka : Figure // просто наследуемся от фигуры.
    {
        public bool isWhite = false; // добавим для шашек цвет - по-умолчанию=чёрный - хотя на самом деле - просто один из двух.
        public Shashka(int x, int y) // создавать фигуру без начального размещения на поле вроде как бессмыслено - поэтому заводим вот такой вот конструктор.
        {
            this.X = x;
            this.Y = y;
        }
        public Shashka() // явный конструктор по умолчанию, чтобы наследоваться.
        {}
        
    }
    public class ProstoShashka :  Shashka
    {
        public override Cell[] CanMoveTo(int x2, int y2)
        {
            Cell[] rez=null; // ответ фигуры на вопрос о способности совершить ход.
            if (this.isWhite == true)
            {
                if ((((this.X - x2) == 1) || ((this.X - x2) == -1)) && ((this.Y - y2) == -1)) //  с помощью этого выражения фигура убеждается что ей предлагают просто походить вперёд(при этом вправо или влево.)
                    rez = GetPath(this.X, this.Y, x2, y2); // получаем траекторию
                else if ((((this.X - x2) == 2) || ((this.X - x2) == -2)) && (((this.Y - y2) == -2) || ((this.Y - y2) == 2))) // так по поей мысли определяет элементарный удар в четырёх направлениях по диагонали ,причём если бы я знал функцию модуля , то написал бы красивее, но функции модуляы я не знаю, а интернет не работает.
                    rez = GetPath(this.X, this.Y, x2, y2);  // получаем траекторию
                return rez;
            }
            else
            {
                if ((((this.X - x2) == 1) || ((this.X - x2) == -1)) && ((this.Y - y2) == 1)) //  с помощью этого выражения фигура убеждается что ей предлагают просто походить вперёд(при этом вправо или влево.)
                    rez = GetPath(this.X, this.Y, x2, y2); // получаем траекторию
                else if ((((this.X - x2) == 2) || ((this.X - x2) == -2)) && (((this.Y - y2) == -2) || ((this.Y - y2) == 2))) // так по поей мысли определяет элементарный удар в четырёх направлениях по диагонали ,причём если бы я знал функцию модуля , то написал бы красивее, но функции модуляы я не знаю, а интернет не работает.
                    rez = GetPath(this.X, this.Y, x2, y2);  // получаем траекторию
                return rez;

            }
            

        }
        static Cell[] GetPath(int x1, int y1, int x2, int y2)  // эту функцию можно будет использовать и для дамки.
        {
            // bool b = false; //используется при определении траектории.
            Cell[] rez = new Cell[0];
            int xz, yz,x,y;
            x = x1; y = y1;
            Array.Resize(ref rez, 1);
            rez[0] = new Cell(x,y);
         //   rez[0].x = x; rez[0].y = y; // траектория как минимум будет выражденной -только исходная клетка.
            if ((x1 != x2) || (y1 != y2))
            {
                if (x1 == x2) xz = 1;
                else // определяем - возрастает или убывает x;
                    xz = Math.Abs(x2 - x1) / (x2 - x1);
                if (y1 == y2) yz = 1;
                else // определяем - возрастает или убывает y;
                    yz = Math.Abs(y2 - y1) / (y2 - y1);
               /* возврат траектории может потребоваться тодько в случае , если фигура "счиатает" ,
                * что она можнет так походить -  сответственно, в шашках достаточно просто "провести прямую" из начала в конец хода - это 
                и будет траетория*/
                while ((x != x2) || (y != y2))
                {
                    x = x + xz; // движемся по прямой (а может и нет если второе слогаемое равно нулю.)
                    y = y + yz; // движемся но прямой (а может и нет если второе слогаемое равно нулю.)
                    Array.Resize(ref rez, rez.Length + 1); // выделяем память для ещё одного элемента массива.
                    rez[rez.Length - 1] = new Cell(x, y); // запоминаем координаты очередной клетки
                //    rez[rez.Length - 1].x = x; // запоминаем координату очередной клетки
                  //  rez[rez.Length - 1].y = y; // запоминаем координату очередной клетки
 
                } // на этом вычисление траектории закончено.

            }
            return rez; // возвращаем массив значений.
        }
       // на этом описание "просто шашки" (не "дамки") можно прекратить и заняться фасадом, который , собственно, и будет руководить игрой.
    }
    //--------------------------------------------------далее - 
    //вспомогательный класс, описывающий клетку - содержит только две координаты  -необходим для передачи траектории хода в виде массива клеток.
    public class Cell
    {
        public  Cell(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        public int x;
        public int y;
    }

    
}

А вот это Наблюдатель - он будет рисовать для нас игровое поле - отображая для пользователя внутренне состоянием игрового мира

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace CheckersPatternGame
{
    public abstract class Observer // шаблон для всех наблюдателей
    {
        public abstract void UpdateView(); // метод который призывает наблюдателя обновиться в соответствии с возможным изменением наблюдаемого.
    }
    public class GameFiledView1 : Observer // наш конкретный наблюдатель.
    {
        public GameFiledView1(Graphics bm, ChakersFacade1 cfd) // наш наблюдатель теряет смысл существования без места, где он может рисовать и натуры, образ которой можно запечатлеть на плоскости.
        {
            this.BitMap = bm;
            this.UpdateSourse = cfd;
           // this.PaintArgs = e;
        }
        Graphics BitMap;// то, на чём мы будем рисовать.
        ChakersFacade1 UpdateSourse; // то, с чем мы будем согласовывать графическое представление (рисование).
      //  PaintEventArgs PaintArgs;
        public override void UpdateView()
        {
            Brush brush1 = new SolidBrush(Color.Chocolate);
            Brush brush2 = new SolidBrush(Color.White);
            Brush brush5 = new SolidBrush(Color.BurlyWood);
            Brush brush3 = new SolidBrush(Color.Black);
            Brush brush4 = new SolidBrush(Color.DarkOliveGreen);
            Pen blackPen = new Pen(Color.BurlyWood, 3);
            Brush brush7 = new SolidBrush(Color.DarkSlateGray);

            // залём фон
            BitMap.FillRectangle(brush4, 0, 0, 1000, 1000);
            BitMap.FillRectangle(brush7, 45, 45, 325, 325);
            // рисуем поле.
            int d = 40; // параметр для рисования.
            int с = 25; // параметр для рисования.
            int h = 8; // параметр для рисования.

            int x = 1, y = 1; //  -координаты.
            bool b = false;
            for (int i = 1; i <= 64; i++)
            {
                if (b == true) BitMap.FillRectangle(brush2, x * d, y * d, d, d);
                else BitMap.FillRectangle(brush3, x * d, y * d, d, d);
                if (x == 8)
                {
                    y = y + 1;
                    x = 1;
                }
                else
                {
                    x = x + 1;
                    if (b == true) b = false; else b = true;
                }
            }

                foreach (Shashka sha in this.UpdateSourse.FigMass)
                {
                   // Rectangle rect = new Rectangle(sha.X * 30, sha.Y * 20, sha.X * 20 + 100, sha.Y * 20 + 100);
                    //Rectangle rect = new Rectangle(50, 50, 158,  158);
                    //   PaintArgs.Graphics.DrawEllipse(blackPen, sha.X * 20, sha.Y * 20, 20,20);
                    if (sha.IsKilled == false)
                    {
                        if (sha.isWhite == false)
                            BitMap.FillEllipse(brush1, sha.X * d + h, sha.Y * d + h, с, с);
                        else
                            BitMap.FillEllipse(brush5, sha.X * d + h, sha.Y * d + h, с, с);
                    }

                }
           
        }
    }
}

вот в этом файле вы используем паттерн Команда - а также создадим класс игрока (кстати число игроков можно было бы ограничить паттерном Одиночка - но мы это тут делать не станем):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CheckersPatternGame // здесь мы соберём набор классов, используемых в патерне "Команда" и те. что относятся и интерфейсу Player.
{   // далее паттерн КОМАНДА
    public abstract class Command  // общий интерфейс для всех команд приложения.
    {
        public abstract void Execute();  
    }
    public class myCheckersCommand1 : Command  // команда выполнения хода.
    {
        private Facade AddresseeFiled; // доступ только через свойство +  адресат , то есть собственно тот класс , которому и будет передаваться команда - это просто наследник от фасада.
        private int x1Filed, y1Filed, x2Filed, y2Filed;
        public Facade Addressee // используем это свойство для получения и смены адресата.
        {
            get { return this.AddresseeFiled; }
            set { this.AddresseeFiled = value; }
        }
        public int x1 // координата начального положения.
        {
            get { return this.x1Filed; }
            set { this.x1Filed = value; }
        }
        public int y1 // координата начального положения.
        {
            get { return this.y1Filed; }
            set { this.y1Filed = value; }
        }
        public int x2 // предполагаемая  координата после выполнения хода.
        {
            get { return this.x2Filed; }
            set { this.x2Filed = value; }
        }
        public int y2 //предполагаемая  координата после выполнения хода. 
        {
            get { return this.y2Filed; }
            set { this.y2Filed = value; }
        }
        public myCheckersCommand1(int x1,int x2,int y1,int y2) // конструктор для создания команды выполнения хода. 
        { // запоминаем координаты хода.
            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;


        }
        public myCheckersCommand1() // явный конструктор по-умолчанию.
        { }
        public override void Execute() // собственно метод посылает команду тому, кто её будет исполнять.
        {
            this.Addressee.Make_a_Move(this.x1,this.y1,this.x2,this.y2); // приказываем фасаду выполнить ход - то есть изменить данные о расположении фигур на поле.
        }
    }
    
    //----------------------------------------------------------
    // Далее определение объекта типа  PLAYER
    public abstract class Player // итак, некий  "интерфейс" для класса игрок, который и будет посылать команды игре, а конктренто - Фасаду.
    {
        private Command MakeAMoveCurCommandFiled; // доступ только через свойство +  адресат , то есть собственно тот класс , которому и будет передаваться команда - это просто наследник от фасада.
        public  abstract void Make_a_Move(); // наш игрок пока что может сделать одно единственное действие - отменить ход.
        public Command MakeAMoveCurCommand // используем это свойство для получения и смены адресата.
        {
            get { return this.MakeAMoveCurCommandFiled; }
            set { this.MakeAMoveCurCommandFiled = value; }
        }
 
    }
    public class myCheckersPlayer1 : Player
    {

        public myCheckersPlayer1(Command cmd)
        {
            this.MakeAMoveCurCommand = cmd; 
        }
        public override void Make_a_Move()
        {
            this.MakeAMoveCurCommand.Execute();
        }
    }
}

Key Words for FKN + antitotal forum (CS VSU):