javaintellij-ideaencapsulation

When is it bad convention to have a Java object visible outside of a root package?


I am building a CLI chess project (my first non-school project). I have a Board class in my GameLogic package which contains a 2D array with Piece objects to represent the board. I also have a ConsoleVisual class which renders the board to the console using StringBuilder. I wanted to structure my game logic in a single package so that everything is properly encapsulated, with as little public access to the inner workings of the game as possible.

My issue is that sending a board object outside the package to aide the ConsoleVisual class is the route of least resistance. Of course, I could just pass a string representation, but I eventually want a GUI, and a string will no longer cut it. It's hard to avoid passing that data outside of the package.

I am having trouble determining if sending this object outside the package would be bad convention. What should my decision making process look like when deciding to send objects outside of a package that you want to be encapsulated?

Current project structure


Solution

  • Encapsulation seeks to avoid revealing the details of an class’ internal implementation. This policy gives the class the freedom to change that implementation without breaking other parts of the app.

    The goal is to prevent the fragility that so often plagues software where an entire app fails after a programmer made “just one tiny change”. Encapsulation, plus automated testing, gives us programmers the confidence that we can rebuild parts of our app without the whole thing collapsing like a house of cards.

    Board class in my GameLogic package which contains a 2D array with Piece objects

    Imagine that you do pass your Board class or its array of pieces to the user-interface. Then, later, you decide to replace that 2D array with a different data structure. BAM! Now your app crashes, or fails to compile, because the UI code is trying to access an array that no longer exists.

    To avoid that brittleness, your Board logic portion of your app can devise a data-structure describing the board’s state to communicate externally to the UI the current conditions in a way that does not unnecessarily disclose internal details.

    Of course the Board must necessarily disclose that it tracks chess pieces, and that it knows the placement of those pieces. But the Board does not need to disclose the use of a 2D array, or a List of Lists, or a Map, or a bit-array, or any other data structure. And certainly the Board does not want give any external agent the power to directly manipulate that internal data structure.

    The solution to the question of how to disclose the chess pieces and their placement information without disclosing internal implementation details is to create an intermediate communication layer.

    For example, you could use Unicode’s chess characters to define a simple COMMA-delimited text-based description of the board in standard chess notation:

    ♖a1,♔a5,…
    

    Now you are free to rewrite your Board class in any fashion you desire, even replace that 2D array, as long as the class continues to emit this same textual description.


    Actually, you need not bother to devise your own chess engine to UI text description, as one has already been invented! The Universal Chess Interface (UCI) is an open communication protocol that enables chess engines to communicate with user interfaces.