This week's book giveaway is in the JavaScript forum.
We're giving away four copies of Cross-Platform Desktop Applications: Using Node, Electron, and NW.js and have Paul Jensen on-line!
See this thread for details.
Win a copy of Cross-Platform Desktop Applications: Using Node, Electron, and NW.js this week in the JavaScript forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic

GUI version of Tic Tac Toe game  RSS feed

 
Lee High
Greenhorn
Posts: 22
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello all, Just reaching out for some help on this project I'm working on. I'm new to this and could use all the help I can get. I've been working on this code for days with no results. All I'm trying to do is have the game tell me whether Nought wins or Cross wins or there is a tie, or if the game needs to continue. I'm really lost and don't know what I'm doing wrong?



Board.java



Mark.java




Outcome.java



Player.java



Cell.java

 
Knute Snortum
Sheriff
Posts: 3940
92
Chrome Eclipse IDE Java Postgres Database VI Editor
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This line: ...is a syntax error.  Please post code that compiles. 
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi,

I think you'd need to narrow down to the one or two issues you want to start solving from. With a question "what I'm doing wrong" you could get terrific answers like: "almost everything you do wrong".

From the quick scan I see your code should not compile. Not sure if that is the biggest your problem at the moment, but definitely one of those.
Actually quite a lot issues there in your code and don't know where to start from. Maybe you could state yourself first, what kind of issues you think you ran into?

And welcome to the Ranch
 
Lee High
Greenhorn
Posts: 22
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello again, Thanks for the feedback. I removed the code that would not compile, but that was the only error I received, when I run the code I can view the board and I am able to place an X or O in the squares, but It will not determine whether X won or O won or I need to continue the game. Most of the codes I have looked up point me in the direction of the code I placed. I think, but I am not sure that for this code to work I would need state it in the takeTurn method? Your feedback would be greatly appreciated.
 
Piet Souris
Rancher
Posts: 1942
66
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
To be frank: in another topic, about the same subject, I pointed OP to some shortcomings in his code, and if he wanted to look at these issues. The next reply was about reefactoring and OOP matters, and if OP wanted to go that route. Unfortunately, all that was done so far was urging OP to search for similar topics. I must say that we haven't helped OP much. Either we help OP fixing his code so that it runs correctly (not too hard), or we go the refactoring OOP way, as promised. Who takes up the glove?
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, here's the thing, Piet:

If I'm reading what you're alluding to correctly, in that thread, OP was advised that his original solution was too complex and that, since it was written with mostly static methods, it was not an OOP implementation as he had claimed. OP then said he'd scrap it and try to make it better. OP then posted another version of code that really wasn't much better.

Now, if Lee wants to keep going down the road he's been told is not a good way to go because it will only lead to more suffering for him, we can't force him to turn back. It has to be his choice to do that. If he wants to learn how to write an OOP solution for this problem and throw away his current code, I'll be happy to pick up the gauntlet and help out. However, since Lee doesn't seem that interested in the things that I'm interested in showing and telling about, I shall stay out of it. If you're fine with helping him with his short term problems while ignoring the big picture problems, I can't do much to stop you. Frankly, however, I see helping him "fix" this messy code as nothing less than helping him tie a proverbial noose around his neck. And honestly, I can't stand looking at that kind of code for very long. You guys know I'm very obsessive-compulsive about clean code and that kind of "stuff" that is posted just eats away at my sanity. <Shrug>
 
Lee High
Greenhorn
Posts: 22
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello all, I am sorry to create such a dilemma. I am new at this and am trying to build my knowledge, and create better code. We have all started somewhere in our training and hopefully I will become as good as your guys in the future. Thank you again for all your help and guidance.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Don't worry about it, Lee. Sometimes old timers around here can't agree on how best to help new guys like you. I just think it's better to start out doing the right things, but then again, I probably wouldn't be where I am today if I didn't make a lot of silly mistakes when I was still learning. In fact, I'm also still learning and when I make mistakes, they seem even sillier to me than if it were a beginner making them. So it's a double edged sword, trying to help you through your current problems, then hoping you'll figure out that the entirety of what you did, in the big picture sense, wasn't really that good even though you got it to work. I don't feel comfortable telling you how to basically jimmy a solution with duck tape and baling wire.

Like I said, if you want to try a better way to solve this, one that is more OO, I'd be willing to get you started. However, I have to warn you that I'm a tough task master and if I don't sense that you're putting as much effort into figuring stuff out, I tend to lose patience and/or interest in going on. So, you up for the challenge (it won't be easy for you)?
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Lee High wrote:Hello all, I am sorry to create such a dilemma. I am new at this and am trying to build my knowledge, and create better code. We have all started somewhere in our training and hopefully I will become as good as your guys in the future. Thank you again for all your help and guidance.

Have a cow, we are with you, and certainly understand your frustration and limited capabilities to do so in a better way.

Could you please provide a bit of background about your current level, studies you took, so we could try assess if it is something you could definitely know by now, or at least start from now.

Piet and Junilu said lots of truth, one from one perspective, another - from other. Both are quite right actually.

The thing is, that personally I, I wouldn't even know how to help repairing here as I couldn't solve myself that probably as it is complicated enough already. There are some parts in a code which I don't think can be repaired, but simply merits deletion.

Such code as below, is so complicated that I'm not sure you even understand that - nor I. Apart from that, it is illegal code, so not much fixing here can be done. Also it involves puzzling parts which even experienced developers could easily slip on it. I have in mind turn++ % == 0

Helping OP to fix this, I'm afraid would be simply give away work as OP seems to be assembled that code from various sources, which gave little to no actual understanding about what he did.
Might OP would like to try to re-do all work, I'd be happy to participate in it too as I never solved that exercise myself. And I know little about the GUIs.

 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Liutauras Vilda wrote:Also it involves puzzling parts which even experienced developers could easily slip on it. I have in mind turn++ % == 0


This is what I like about you, Liutauras. You are very self-deprecating and yet this comment tells me that you have developed quite a nose for code smells. You're right, it is too complicated of an expression and the whole two lines of code gets into too much implementation detail.

Something like this expresses pretty much the same ideas:

The nitty-gritty details are hidden in methods with names that express their intent. This code would be much easier to read than what you quoted, and you could probably figure out a better way to implement these ideas.  When you start from the bottom up without an idea of what your "big picture story" is, you often end up with code that's confused and convoluted. With no big picture to guide you to your destination, you'll just flap around like a fish out of water, trying to get back in the pond.
 
Lee High
Greenhorn
Posts: 22
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
all valid responses and I appreciate it. This is my first GUI code and have no experience at it. I will take all your advisements and rethink my code. Knowledge it power and hopefully I will develop that power moving forward. Thank you again.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
As an example of what I meant by finding a better implementation and keeping the "big picture story" in mind to guide you to the correct implementation:

When I read this code, I'd probably think, why do these names all have game in them? Maybe this would be better:

This exercises your ability to detect the code smell of mis-assigned responsibility (the name of a class embedded in a method name sometimes indicates that the logic belongs in that other class). So, in client code, instead of having this:

you would have this:

This last version is more object-oriented.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Programming is a thought process. It's also a process of discovery, of refining ideas, and reorganizing your thoughts. Effective OOP requires a certain agility in your mindset. If you come up with an initial idea, it's usually not OOP. See my example. Even though I offered it as a better alternative to what OP wrote, it wasn't very OOP either. But writing it down in code helped me see the problem much more clearly and helped me ask the right questions. The problem beginners have is that they don't know what the right questions to ask are. They need other people to show them what to ask. If the beginner is observant enough, they will be able to see things from a different perspective and start asking the same kind of right questions themselves. This is very similar to how I learned martial arts. You need to see what other people are doing, experience it yourself by going through the same motions. Much like in programming, just going through the mechanical movements doesn't help you much but it's a start. What really helps you learn is making yourself go through the same thought process.

So, this is kind of how my thought process goes when I see code like this:

Yuck. That is messy, convoluted, and filled with implementation details. What is the intent? Let's try this:

Then, "Hmmm, why do all those methods have 'game' in the name? Maybe that logic should go in a Game class..."

Ok, now we're getting somewhere...

(and so on...)

It's not enough to read these things I'm illustrating. See my signature below. You can't learn how to swim by watching others do their thing in the water. You have to get in the pool and paddle around yourself. Likewise, you have to make yourself go through a similar thought process of discovery, reasoning, reorganizing, and learning. Then only will your brain start to find its way around the complex and jumbled set of ideas floating around in your head. Programming is very difficult to do as a thought experiment. You have to experiment with written and running code and see what it actually does before you can make adjustments to make it better.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Now, let's look at this code:

I'll give you three guess as to what my reaction would be on seeing this code...

If you said, "Something along the lines of, 'YUCK! This code is messy, convoluted, and full of implementation details'," you'd be right.

What if we tried this instead:

You might ask "Where did all the other stuff go?" Well, is it really good to pull out information of the Cell or should we just have a Cell return its representation as a String, based on what mark it currently has? If you answer "Yes, it's better to have the Cell return its representation as a String so that information it has doesn't have to be pulled out of it" to this question, then the code you're looking for is going to be in the Cell class, not here.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This code is a perfect example of how many programmers struggle to find the correct words to use to express the ideas they have in their code:

On line 40, why is the loop variable called cell? Does that variable really represent a single cell? Why is the type Cell[] then?

On line 42, we see an even worse case of what I'll call the "I don't know what to call this so I'll just call it whatever" programmer syndrome. The confusion follows from the bad name of cell. The name cell would actually make sense to use as the loop variable on line 42 but since that name was already used on line 40, you can't use it anymore! Rather than go back and fix the problem in line 40, the programmer decides to cop out and just "conveniently" tack on a meaningless number "1" to the name, hence, you get the equally meaningless name of cell1.

If you had gone back and fixed the name on line 40 instead, and made it match the thing it represented, which is a row of cells, then you are now free to use the totally logical name of cell on line 42.

See how seemingly little "trivial" things like names can matter so much in making your program make sense?

One of our moderators here has a tagline on their signature that says "The two hardest problems in computer science are: cache invalidation, choosing good names, and off-by-one errors."  I would add that by far, choosing good names is the hardest of those.

By the way, that code now "reads" like this:

for each row in this grid of cells, do the following:
   ...
   for each cell in the current row, do the following:
      ...

Reading that out loud, you might recognize that a better name for cells might be grid or board so then, a reading of the code would make much more sense:

for each row on the board, do the following:
   ...
 
Lee High
Greenhorn
Posts: 22
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That information is so helpful, I do find it some what difficult to choice the correct name. I'm going to relook at my code and utilize this information thank you again.
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Lee High wrote:That information is so helpful, I do find it some what difficult to choice the correct name. I'm going to relook at my code and utilize this information thank you again.

I read few time what Junilu wrote lately. There are definitely lessons to take from there. Now, Lee, no one expects you to jump and write high quality object oriented code, that probably would be mission impossible for now, BUT, you should not stop from trying.

What I'd find myself easier to move towards OO (object-riented) code from your variant, is as a first step to break down the code into lots of small methods (once again Junilu's example) :

Even though it isn't object oriented code yet, but the transition from that to OO rather from your variant is way shorter. Why? In most cases probably you'd find that you need to cut n' paste methods into the right classes once you identify what these might be. If you just just started, yes, you can feel it's might too early, but you have created already few classes there, so it seems it is exactly right time to take a lead and implement this game better way than you have now.

Just try, you might find it way easier thank you think.

Lee, I don't get at all, why there are so many different marks? Shouldn't be just 3 of those? Nevermind probably.


Important thing. Look to your Cell class. It has method getRow(), getColumn() - don't you think it can confuse you at some point thinking about Row of Cells AND at the same time having method getRow(), which indeed returns not a row, but rather an index of the row within a Board where Cell appears? (it isn't might confusing when you look to one context, but when the code interpolates it can lead to confusion I think). I think you'll need to re-think slightly that part. Same problem with getColumn(). Simply improving method's name could significantly improve clarity.

One more thing. I'm not sure I'd have players as enum. Players in some sense interacting with game, or more precicely they are playing the game. To have them just simply as constants which do nothing seems like not much beneficial. Who does the move or marking, computer or player? If player, maybe you need a Player class too?

So you could have something like (just dropping ideas, might some of those you'll find valid after a try) :
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Liutauras Vilda wrote:
Lee, I don't get at all, why there are so many different marks? Shouldn't be just 3 of those? Nevermind probably.


This is just a mixing of two different ideas. Lee still needs to distill this and separate the ideas. The main idea is, of course, Mark {EMPTY, NOUGHT, CROSS}. The other idea is Color, represented by the rest of the values defined. In Lee's mind, these are related because he can use these colors to draw a cell that is empty, or has a NOUGHT or CROSS on it. His mistake is that he mixes them all up into one enum type definition. The mixing of ideas like this will cause confusion in your mind that is then going to be reflected in the code.

The mixing of concerns send the same kind of crossed signals through your brain as it would if you tried to draw vertical squares in the air with your left hand and horizontal circles with your right hand. It's hard and most people can't do it. If you can manage to do it, try again but switch hands or switch the orientation of the shapes this time.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Lee High wrote:
One more thing. I'm not sure I'd have players as enum. Players in some sense interacting with game, or more precicely they are playing the game. To have them just simply as constants which do nothing seems like not much beneficial. Who does the move or marking, computer or player? If player, maybe you need a Player class too?

These are good questions. I often say that it's a trap to try to model your program too closely to the real world. Taking cues from the real world can be useful when creating a model. However, you're not trying to replicate the real world in your program model. When it comes to programming, modeling is not the same as replicating. The goal of modeling in a program is to find useful abstractions.

I would agree with having a Mark enum to represent the idea of a "mark" that is put on a cell. EMPTY, NOUGHT, and CROSS are useful abstractions. They represent what we would see in a pen and paper game of TicTacToe. I would question the need for a Player class though. Apart from maybe keeping track of real-world player names, of what use is it in tracking the state of the game? I'd say it wouldn't be much use.

Think about this: When you're playing the game, you, the person playing, are often referred to by the mark you are making in the game, not by your name. If someone comes up to you and asks you, "Which one are you?" you don't answer "I'm Lee," you would answer "I'm X," or "I'm O." You actually map your identity to the mark that you are making in the game. Also, anyone who doesn't know either of the players can still look at the board and confidently say things like, "X is going to win" or "O is going to win" or "Neither X nor O is going to win." They don't need to refer to any of the players by name, they just need to refer to the mark the player is using because each player's identity is mapped to the mark they are making. So, translating this idea to code:

Can you see how we might implement the game.wasWonBy() method? It's actually pretty easy and it also demonstrates how you can avoid using a getter:

Now, contrast that with this design:

Here, you are pulling information out of the Game object and doing some calculation with it. This is not as object-oriented as saying if (game.wasWonBy(player1))

The implementation of the Game based on this design would need that getter, which in a way, breaks encapsulation.


If you notice, this implementation differs from the previous one in that it does not have a wasWonBy(Mark) method. The code that results from this design is less object-oriented. The richness and expressiveness of the Game object semantics is also lesser in this design that it is in the other one.

But wait, it could be even worse if you had started out with a getWinner() method in your Game class design:

This code is NOT object-oriented at all, even though you may be using a Game object. All the logic that should be in the Game object is now externalized from it. This is what PROCEDURAL code in Java looks like.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Notice, too, how small the methods can become when you have a more object-oriented design. I find that the more object-oriented you make your design, the smaller your methods tend to be. In fact, most methods in my designs that adhere to object-oriented principles tend to be only a few lines of code on average. I have honed this over the years, chasing Kent Beck's ideal of many single-line methods that express their intent well in the method names.

One of Kent Beck's "Four Rules for Simple Design" can be interpreted as "Keep things small." In other words, keep interfaces small (Interface Segregation Principle), keep methods small (Single Responsibility Principle), classes small (SRP again), keep the scope of methods, variables, etc. as small as possible, etc.

I have read (or heard maybe) that on average, Kent Beck's methods have about 1-5 lines of code. Or so they say. I don't know this for sure; maybe it's just an urban legend at this point. However, in chasing that ideal, I found that it is achievable. I can't say I write all my methods like that but when I have a method that gets to more than 10 lines of code, I'm always looking for ways to distill the intent and refactor for clarity by extracting method(s). That usually brings me down to under 10 lines of code.

The exception to my obsessive-compulsive quest to have small methods is when I need to think about performance. Having too many method calls might impact performance. However, I will always factor the code for clarity and expressiveness first. If necessary, you profile the code so you can see if the increased number of method calls actually do impact performance under the same kind of conditions that you need your code to perform within certain parameters. NEVER optimize just because you think it might help performance. That's premature optimization and it's considered evil in many programming circles. Programmers suck at optimizing by intuition and gut feeling. Don't be evil and do that kind of optimization.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You may think that the difference in the two designs, having a boolean wasWonBy(Mark) method versus having a Mark getWinner() method was not that big of a deal. In some cases (maybe even many), if you're careful and if your program is small enough, you can get away with the less object-oriented design and still not produce massively smelly or procedural type code.

However, I go back to the richness of expression that the more object-oriented design affords the rest of the code. The idea of who won is encapsulated in the Game object in the first design. When you start writing a lot of code that looks like this:

and you see it a number of times in your code, then it starts to get smelly because it violates the DRY Principle (Don't Repeat Yourself). In the code that relies on the getter() method, the knowledge of who won the game and/or whether or not some player was the winner is now externalized from the Game object and this makes you have to keep writing the same expression every time you want to access that knowledge.  You might say "Well, I'd just extract all that to a method" and do something that we've already discussed before:

and you might go merrily on your way, still not recognizing that this method is unnecessary here and it really should be in the game class.

I can tell from experience that these things add up in the long run. The smell of the above workaround will still surface when you go to another class, encounter the same kind of duplication as before and say, "Hey, I'll just extract it to an isWinner() method here."  Now you have two classes with the same isWinner(Mark) method.  The astute programmer will recognize this as again a violation of DRY and figure out why that is.  The naive or uncaring programmer just shrugs and keeps going, leaving behind the faint odor of a DRY violation that could soon be added to in another class and yet another isWinner() method there.

Do you see how smells that you try to workaround rather than address properly never really go away, even if you temporarily mask it with an extract method refactoring that didn't quite go as far as it could have?
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think I just realized how to explain how a getter can violate an object's encapsulation.

Consider my example again:

A getter method has the potential to violate an object's encapsulation if it gives out RAW values of the object's state that will then be interpreted in some way by some external code.  This code is one such example:

This code performs a calculation using RAW data from a Game object. The idea involved is "whether or not a certain player won the game" and that idea is expressed by the if-statement's condition.

In contrast, this code doesn't use any raw data obtained from the Game object to make that determination.

This version preserves the Game object's encapsulation because we didn't have to pull any raw information out of it and make any external calculations/determinations. We just told the Game object to check if the value we are giving it is in fact the winner of the Game. We let the Game object do all the dirty work of doing calculations and comparisons by accessing its own internal state, then telling us what the result of that calculation/determination was.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
My son just suggested a much better method name to me:

From the mouths of babes...
 
Stephan van Hulst
Saloon Keeper
Posts: 7711
141
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Don't agree. It reads like a statement and not like a question. I would roll the two methods into one and return an Optional.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think the "if" part of the statement and the fact that the method returns a boolean makes it natural to interpret as a query. I haven't experimented with using an Optional in this context but it seems a bit much. Also, not many people are familiar with Optional or are only familiar with it in the context of Streams, so that might raise even more questions. Shrug. I like the name. You don't have to agree, that's fine.

Edit: You've convinced me about Optional, especially with the usage that you show below. Very elegant.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I just experimented with Optional and you may have a point. This looks pretty encapsulated as well:

That definitely looks like nice, object-oriented code. Thanks for the suggestion.

Edit: Although when I put the two side-by-side, I still like this version:

This version is more abstract and doesn't need any mention of Optional or an understanding of its isPresent() and equals() methods.
 
Stephan van Hulst
Saloon Keeper
Posts: 7711
141
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Or:
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:Or:

That is definitely nice and elegant. Awesome feedback. Thanks!

(and have a cow for that suggestion, too)
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan's suggestion to use Optional for winner actually makes a lot of sense now that I've seen the possibilities.

Which leads me to think that the enum Mark should be defined only as:

The EMPTY value can be represented however it makes sense to represent an open spot on the board, perhaps null or a space, but that now becomes a choice that's made in the Game class itself and not part of the Mark enum.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Notice that when you distill all these different ideas into their own methods, there's not a lot of code. These are all one-liners. And I didn't really try to make them one-liners. I just concentrated on stating the ideas in the method names. The implementations then followed naturally and logically.

I have found that when you start digging into the details right away, which for some reason is the natural tendency of many programmers, you start losing yourself in the weeds of complexity and your code starts to look like you threw up all over the keyboard and just started trying to wipe stuff away as you programmed, and then all your vomit somehow ended up as code. Sorry to paint such a gross picture but honestly, that's what I see a lot of messy code as, just programmer projectile vomiting whatever crazy idea comes their minds and never even bothering to clean up after themselves. 

This is usually what I look like when I see bad code:

And then when I have to go into that code and clean it up...    
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I'm glad OP started all this. I have learned a lot, hopefully OP too. Nevertheless, this thread has been viewed few hundred times, seems many find something in it to learn
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I agree, it was good that OP started this thread. I'm also glad Piet responded with the challenge to pick up the gauntlet, otherwise, this thread might have just withered and died on the vine. I definitely learned something here, too.

I even related what I learned to Jerry Weinberg's, Weinberg's Law of Fetch: http://secretsofconsulting.blogspot.com/2017/05/why-i-stopped-being-professor.html?platform=hootsuite

"Sometimes farfetched is only shortsighted."

My translation in the context of this thread: Never dismiss an idea that seems far-fetched until you give it a try. It just might be the thing to cure your shortsightedness.

That's what almost happened with me on reading Stephan's suggestion to use Optional. It seemed a bit of a stretch to me at first but then when I tried it out, it was clear that it was just my own shortsightedness and perhaps getting too enamored with the code I had come up with.

Aikido principles come into play again. Try to tenkan and see the world from the other person's perspective. This will almost always open your eyes and mind to a different point of view.

It's all good. Thanks to everyone who has participated so far!
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Very nice snippet.
Stephan wrote:

For this particular code, I just would change slightly getWinner() method to announceWinner(), because the ending result even tho it returns winner, final result is that it passes to lambda which prints it.

So, probably naturally I'd write:
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think I know where your brain is going with that Liutauras but the idea of "announcing the winner" is actually embodied in the lambda expression that you pass to the Optional.ifPresent() method, not in the method that returns that Optional. This makes more sense:

Do you see where the idea that you had fits? Basically, what that line of code says is this: "Get the game's winner and if there is one, announce who that winner is."
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Alright, I see, I think as for beginning with Optional, separating onto different lines eliminates any unclear things one might have.
But functional paradigm has its own style to shorten code and use in particular short variable names which not always superb clear.

If anybody else didn't look to documentatin, here it is what Junilu showed:
JavaDocs wrote:ifPresent(Consumer<? super T> consumer)
If a value is present, invoke the specified consumer with the value, otherwise do nothing.
 
Liutauras Vilda
Marshal
Posts: 4483
304
BSD
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
JavaDocs wrote:ifPresent(Consumer<? super T> consumer)
If a value is present, invoke the specified consumer with the value, otherwise do nothing.

I find this idea of chaining similar to idea used in Kotlin language.

KotlinDocs wrote:Safe calls are useful in chains. For example, if Bob, an Employee, may be assigned to a Department (or not), that in turn may have another Employee as a department head, then to obtain the name of Bob's department head (if any), we write the following:

bob?.department?.head?.name

Well, latter example is about null safety, but the final result is kinda similar when I think now.

edit: probably not exactly similar whole idea. In particular ifPresent() probably plays similar role as in Kotlin null safety
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You could probably define a whole bunch of Consumer<Mark> lambdas to do different things. You can even make the consumer something that processes a series of commands related to the winner. You can go hog wild with this idea, actually. I like it a lot because it's all nice, clean, elegant code still.

Caveat: above code is untested. This is just off the top of my head. Command is just an imaginary class that has an execute method that takes a Mark. You could also define the list as List<Consumer<Mark>> and just add different lambdas, each one calling out to other parts of the program to do their little things. Like I said, you can go hog wild with this concept and it still wouldn't be really messy. It might get a little hard for the uninitiated to follow but once you see and understand the flow of the pattern, it still makes sense.
 
Junilu Lacar
Sheriff
Posts: 10948
158
Android Debian Eclipse IDE IntelliJ IDE Java Linux Mac Spring Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Liutauras Vilda wrote:edit: probably not exactly similar whole idea. In particular ifPresent() probably plays similar role as in Kotlin null safety

I agree, the ideas of null safety in Kotlin and the use of Optional in Java 8 is pretty much the same. Kotlin's null safety reference has a little more to it but that's built into the language.
 
Lee High
Greenhorn
Posts: 22
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
First I like to say that this information is GOLD!!! And it is a great help to a newbie like me. I'm soaking up this knowledge and using it towards the Frankenstein code I have created. Thank you all for you knowledge and education. I know a lot of your knowledge comes from experience, but all I have is Java A beginners's guide (sixth edition), and the oracle website. Could you guys recommend any other books or website that would help my studies? 
 
Piet Souris
Rancher
Posts: 1942
66
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Lee, Junilu, Liutauras,

I apologize for my somewhat rough reply, but the effect was exactly what I was hoping for (and, to be honest, I expected no less)

Have all a cow (but given my 1-cow/day limit, that'll take three days).

This is coderanch at its best.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!