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).

0

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).

Jestem tego świadom. W tym momencie plansze przekazuje jako argument do funkcji, ktora okresla, ktore ruchu sa dozwolone.
Ruchy dla kazdej bierki (z wyjatkiem Queen wlasnie) sa tak zroznicowane, ze uznalem, ze moge je po prostu przypisac dla kazdej bierki osobno, bez dzielenia tego na "vertical, ...".

Co do historii rozgrywki - to mysle, ze mimo wszystko nie jest to konieczne. Da sie wszystkie potrzebne informacje przechowywac na obecnej planszy, no moze w wyjatkiem remisu po 50 ruchu bez zbicia i posuniecia pionem - to moze byc problematyczne.

0

Na co tak kombinować?
Zwykła, klasyczna obiektowość sprawdza tu się doskonale.

Klasa Piece z abstrakcyjną metodą wirtualną FindMove.
Klasy pochodne Bishop, Queen &c. każda z własną wersją metody FindMove().

I nie rock tylko rook.

0

@Azarien - przeoczyłeś to samo, co ja kilka godzin temu. Queen potrzebuje zsumować ruchy bishopa i rooka, a nie osiągnie tego dziedziczeniem.

0
JeloneK napisał(a):

Co do historii rozgrywki - to mysle, ze mimo wszystko nie jest to konieczne. Da sie wszystkie potrzebne informacje przechowywac na obecnej planszy, no moze w wyjatkiem remisu po 50 ruchu bez zbicia i posuniecia pionem - to moze byc problematyczne.

No i jeszcze wiecznego szacha, możliwości wykonania roszady, ale nie, nie, historia rozgrywki nie jest przecież konieczna. :D :D :D

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