C#, private methods, how to use in other class ?

0

Mam klasę abstrakcyjną Figura, która zawiera abstrakcyjną metodę FindMove()

Po klasie figura dziedziczą klasy: Rock, Bishop, Queen.

W klasach Rock implenetuje metode prywatna FindRockMove(), która potem jest wywoływana w metodzia FindMove() klasy Rock.
Same for Bishop.

Jaki jest najbardziej właściwy sposób na wykorzystanie tych metod w metodzie FindMove() klasy Queen?

0

A na czym konkretnie polega problem? (Pomijając mieszanie polskiego z angielskim w kodzie.) W Rock.FindMove możesz przecież bez problemu skorzystać z Rock.FindRockMove, w końcu jesteś w jednej klasie.

0

Znowu za mało precyzyjnie się wyraziłem :(

W klasie Queen chciałbym wywołać metody Rock.FindMoveRock() oraz Bishop.FindMoveBishop().
Oczywiście mógłbym np. te metody przekopiować, czy zmienić poziom dostępu na public, ale interesuje mnie, czy istnieje jakiś bardziej wysublimowany sposób.

PS. W zasadzie, to klasa bazowa zwie się 'Figure' (ang) :)

0

Bardziej "wysublimowany" sposób istnieje i nazywa się protected.
BTW Rock.FindMoveRock(), Bishop.FindMoveBishop() to moim zdaniem zła praktyka programistyczna. Powinno być Rock.FindMove() oraz Bishop.FindMove(), a wszystkie metody FindMove dla wszystkich klas powinny być wymuszone interfejsem, w ostateczności klasą abstrakcyjną.
Dlaczego jedna figura (Queen) ma ruszać innymi (taką intencję wyczytałem z pytania o "sposób na wykorzystanie tych metod w metodzie FindMove() klasy Queen")? Pionkami i figurami rusza gracz i to on powinien być "świadomy" istnienia możliwości ruszania obiektami.

0

Bardziej "wysublimowany" sposób istnieje i nazywa się protected.

a protected nie tyczy sie tylko zależnosci klasa bazowa - klasa pochodna ?
Tymczasem klasy Rock, Bishop, Queen wywodza sie bezposrednio od klasy bazowej Figure, wiec sa w pewnym sensie na tym samym poziomie.

BTW Rock.FindMoveRock(), Bishop.FindMoveBishop() to moim zdaniem zła praktyka programistyczna. Powinno być Rock.FindMove() oraz Bishop.FindMove(), a wszystkie metody FindMove dla wszystkich klas powinny być wymuszone interfejsem, w ostateczności klasą abstrakcyjną.

Ależ wlasnie tak sie to odbywa - klasa abstrakcyjna Figure, ktora zawiera metode abstrakcyjna FindMove()
z tym, ze metoda ta w poszczegolnych klasach jest dosc rozbudowana, dlatego podzielilem ja na funckjie, stad metoda FindeMoveRock()

w klasie Queen natomiast juz nie ma potrzeby tworzyc takich dodatkowych funkcji, bo sposob poruszania sie jest polaczeniem sposobu poruszania sie Rock i Bishop

Wiec zwyczajnie chce w klasie Queen, w metodzie (override) FindMove() wywolac metody FindeMoveRock oraz FindMoveBishop, ktore to sa metodami zdefiniowanymi tylko i wylaczenie w klasach odpowiedni Rock, Bishop.

0

Dlaczego jedna figura (Queen) ma ruszać innymi (taką intencję wyczytałem z pytania o "sposób na wykorzystanie tych metod w metodzie FindMove() klasy Queen")? Pionkami i figurami rusza gracz i to on powinien być "świadomy" istnienia możliwości ruszania obiektami.

Jedynym zadaniem metody FindMove() jest określenie, które pola są w danej sytuacji (w zależności o ułożenia figur na planszy) dostępne dla danej figury (tj. na ktore pola ta figura moze sie przemiescic).
Po to to, aby w przypadku kiedy uzytkownic bedzie chcial wykonac niedozwolony ruch - program bedzie o tym wiedzial i nie pozwoli na to.

0

Może więc będzie bardziej zrozumiałe o co mi chodzi, kiedy wstawię kod.
Może przy okazji wyjdą jakieś inne rażące błędy, które popełniłem:

Klasa Figure:

namespace CSharpSecondTry
{
    public abstract class Figure
    {
        protected readonly int _boardSize;

        protected Figure(int boardSize)
        {
            _boardSize = boardSize;
        }

        public abstract int[,] FindMove(int rank, int file, int[,] piecesBoardState);

        protected static void FindRange(int pieceValue, EPiece blackPieceValue, EPiece whitePieceValue, out int rangeStart, out int rangeEnd)
        {

            if      (pieceValue == (int)blackPieceValue)
            {
                rangeStart = (int)EPiece.wStart;
                rangeEnd =   (int)EPiece.wEnd;
            }
            else if (pieceValue == (int)whitePieceValue)
            {
                rangeStart = (int)EPiece.bStart;
                rangeEnd =   (int)EPiece.bEnd;
            }
            else
            {
                rangeStart = -1;
                rangeEnd   = -1;
            }
        }

    }
}

Klasa Rock:

namespace CSharpSecondTry
{
    class Rock : Figure
    {
        public Rock(int boardSize)
            :base(boardSize)
        {

        }

        public override int[,] FindMove(int rank, int file, int[,] piecesBoardState)
        {
            var boardState = new int[_boardSize, _boardSize];
            int rangeStart;
            int rangeEnd;

            FindRange(piecesBoardState[rank, file], EPiece.bRock, EPiece.wRock,
                      out rangeStart, out rangeEnd);

            FindMoveRock(ref boardState, rank, file, piecesBoardState, rangeStart, rangeEnd);

            return boardState;
        }

        private delegate void UpOrDown(ref int i);
        private void Incr(ref int i) { ++i; }
        private void Decr(ref int i) { --i; }
        private void Const(ref int i) { }

        private void FindMoveRock(ref int[,] boardState, int rank, int file, 
                                  int[,] pBS, int rangeStart, int rangeEnd)
        {
            UpOrDown delRank;
            UpOrDown delFile;

            delRank = Incr;
            delFile = Const;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);

            delRank = Decr;
            //delFile = Const;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);

            delRank = Const;
            delFile = Incr;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);

            //delRank = Const;
            delFile = Decr;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);
        }

        private void FindWithDel(ref int[,] boardState, int rank, int file, int[,] pBS,
                                 int rangeStart, int rangeEnd, UpOrDown delRank, UpOrDown delFile)
        {
            int i = rank;
            int j = file;

            delRank(ref i);
            delFile(ref j);

            while(i > -1 && i < _boardSize && j > -1 && j < _boardSize)
            {
                if (pBS[i, j] == (int)EPiece.empty)
                {
                    boardState[i, j] = 1;
                }
                else break;

                delRank(ref i);
                delFile(ref j);
            }

            if ((i < _boardSize) && (i > -1) && (j < _boardSize) && (j > -1) &&
                ((pBS[i, j] >= rangeStart) && (pBS[i, j] <= rangeEnd)))
            {
                boardState[i, j] = 2;
            }

        }

    }
}

Klasa Bishop:


namespace CSharpSecondTry
{
    class Bishop : Figure
    {
        public Bishop(int boardSize)
            :base(boardSize)
        {

        }

        public override int[,] FindMove(int rank, int file, int[,] piecesBoardState)
        {
            var boardState = new int[_boardSize, _boardSize];
            int rangeStart;
            int rangeEnd;

            FindRange(piecesBoardState[rank, file], EPiece.bBishop, EPiece.wBishop,
                      out rangeStart, out rangeEnd);

            FindMoveBishop(ref boardState, rank, file, piecesBoardState, rangeStart, rangeEnd);

            return boardState;
        }

        private delegate void UpOrDown(ref int i);
        private void Incr(ref int i) { i++; }
        private void Decr(ref int i) { i--; }

        private void FindMoveBishop(ref int[,] boardState, int rank, int file, 
                                    int[,] pBS, int rangeStart, int rangeEnd)
        {
            UpOrDown delRank;
            UpOrDown delFile;

            delRank = Incr;
            delFile = Incr;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);

            //delRank = Incr;
            delFile = Decr;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);

            delRank = Decr;
            //delFile = Decr;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);

            //delRank = Decr;
            delFile = Incr;
            FindWithDel(ref boardState, rank, file, pBS, rangeStart, rangeEnd, delRank, delFile);
        }

        private void FindWithDel(ref int[,] boardState, int rank, int file, int[,] pBS, 
                                 int rangeStart, int rangeEnd, UpOrDown delRank, UpOrDown delFile)
        {
            int i = rank;
            int j = file;

            delRank(ref i);
            delFile(ref j);

            while(i > -1 && i < _boardSize && j > -1 && j < _boardSize)
            {
                if (pBS[i, j] == (int)EPiece.empty)
                {
                    boardState[i, j] = 1;
                }
                else break;

                delRank(ref i);
                delFile(ref j);
            }

            if ((i < _boardSize) && (i > -1) && (j < _boardSize) && (j > -1) &&
                ((pBS[i, j] >= rangeStart) && (pBS[i, j] <= rangeEnd)))
            {
                boardState[i, j] = 2;
            }
        }
    }
}

Klasa Queen:

namespace CSharpSecondTry
{
    class Queen : Figure
    {
        public Queen(int boardSize)
            :base(boardSize)
        {

        }

        public override int[,] FindMove(int rank, int file, int[,] piecesBoardState)
        {
            var boardState = new int[_boardSize, _boardSize];

            /*
            Using some methods HERE!
            */

            return boardState;
        }
    }
}
1

Nie tędy droga. Nie po to metody mają modyfikatory dostępu, żeby je omijać. Dziedziczenie tutaj nic nie da, więc faktycznie protected odpada.
Pozostaje

  • kompozycja, wywołanie metod (internal tutaj zamiast protected) klas Bishop i Rock i zsumowanie otrzymanych list pól;
  • wydzielenie osobnej klasy (serwisu), która będzie odpowiedzialna za wyliczanie miejsc do potencjalnego ruchu. Tutaj figura mogłaby być opisana bardziej abstrakcyjnie - w którym kierunku może się ruszyć i o ile pól, acz być może ta dodatkowa abstrakcja jest trochę na wyrost patrząc z punktu widzenia wydajności. Jeśli jednak byś się na to zdecydował, to taki resolver zwracałby wszystkie warianty ruchu według danej strategii, inny resolver zwracałby ruchy związane z biciem (o ile mówimy o czymś podobnym do szachów), a w klasie implementującej ruch konkretnego typu figury łączyłbyś takie ruchy. Dzięki takiemu rozdziałowi byłbyś bliżej wzorca SRP.
1

Ja bym zrobił klasę opisującą dowolny ruch czymkolwiek po planszy - MoveDescription, zawierającą pola w rodzaju SqareFrom, SquareTo oraz wyliczane właściwości typu IsHorizontal, IsVertical, IsDiagonal. Konkretna bierka może mieć metodę IsValidMove(MoveDescription) i tam logikę sprawdzającą, czy proponowany ruch jest prawidłowy dla niej. W przypadku np. gońca wystarczy sprawdzić, czy MoveDescription.IsDiagonal jest true.

Oddzielenie opisu ruchu od opisu bierki jest konieczne, bo bierka sama w sobie nie jest w stanie stwierdzić, czy dany ruch jest prawidłowy. Do tego trzeba mieć pojęcie o sytuacji na całej planszy (np. czy ruch nie odsłoni króla) oraz znać historię rozgrywki (np. żeby nie wykonać drugi raz roszady).

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0