2

Code with near-identical blocks like this makes me cringe. Plus it adds up to where you have a thousand lines of code where half that would suffice. Surely there is a way to make a loop to make it all happen and not have code that looks so unsophisticated and brainless.

Offhand it seems like to do so would be adding as much code as I seek to reduce: loop to make 5 buttons, array of labels for the buttons, array of backgrounds... maybe more. Even if that turned out to be acceptable, how would I make a loop to handle the listeners? I can't have an array of methods, can I? I guess such a loop it would have to include a switch. Yes? I'd probably do that if I didn't want to seek a better solution. So I'm asking...

What would code look like that would listen to the entire group of buttons and take action based on which one was pressed? To which component would I assign the single listener? And how?

(There's a chance that the answer to that question will make me cringe even more than the repetitive nature of the code, if I realize that I already know how to do so and needn't have even asked in the first place, but I'm asking anyway. I'm at one of those I've-had-it-for-today points where the brain just wants out.)

  private void makeScoremasterBonuses(){
    pnlBonuses = new JPanel(new GridLayout(1, 6));
    pnlBonuses.setSize(6,1);

    JButton t1 = (new JButton("3W"));
    t1.setToolTipText("This is a triple-word cell.");
    t1.setBackground(TRIPLE_WORD);
    t1.setHorizontalAlignment(JButton.CENTER);
    t1.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        Highlighter.shadeSymmetric(currentCell,TRIPLE_WORD);
      }});

    JButton t2 = (new JButton("3L"));
    t2.setToolTipText("This is a triple-letter cell");
    t2.setBackground(TRIPLE_LETTER);
    t2.setHorizontalAlignment(JButton.CENTER);
    t2.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        Highlighter.shadeSymmetric(currentCell,TRIPLE_LETTER);
      }});

    JButton t3 = (new JButton("2W"));
    t3.setToolTipText("This is a double-word cell");
    t3.setBackground(DOUBLE_WORD);
    t3.setHorizontalAlignment(JButton.CENTER);
    t3.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        Highlighter.shadeSymmetric(currentCell,DOUBLE_WORD);
      }});

    JButton t4 = (new JButton("2L"));
    t4.setToolTipText("This is a double-letter cell");
    t4.setBackground(DOUBLE_LETTER);
    t4.setHorizontalAlignment(JButton.CENTER);
    t4.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        Highlighter.shadeSymmetric(currentCell,DOUBLE_LETTER);
      }});

    JButton t5 = (new JButton(""));
    t5.setToolTipText("No bonus");
    t5.setBackground(WHITE);
    t5.setHorizontalAlignment(JButton.CENTER);
    t5.addActionListener(new ActionListener() {
      @Override public void actionPerformed(ActionEvent e) {
        Highlighter.shadeSymmetric(currentCell,B_NORMAL);
      }});

    pnlBonuses.add(new JLabel("Legend: "));
    pnlBonuses.add(t1);    
    pnlBonuses.add(t2);     
    pnlBonuses.add(t3);     
    pnlBonuses.add(t4);     
    pnlBonuses.add(t5);

  }

I'm not asking anyone to write the code; I wouldn't even want that (but I couldn't ignore it!).

Here's what the code above does: enter image description here

bjb568
  • 11,089
  • 11
  • 50
  • 71
DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • Are the JButtons in any sort of Controls collection? How many buttons are we talking about? If it's no more than four, it's not worth it to try and consolidate the code. – Robert Harvey May 30 '14 at 21:11
  • @Robert--What you see is what I have. I don't really know exactly how to answer you, though. So I guess it's "No". And I guess I should Google "Controls collection" and start studying from there. I've begun making an array of buttons, labels, and backgrounds but still have the question about one listener for all. Thanks for replying. – DSlomer64 May 30 '14 at 21:14
  • 1
    @DSlomer64 Take a look at Qwerky's answer to this question: http://stackoverflow.com/questions/5936261/how-to-add-action-listener-that-listens-to-multiple-buttons – Alan May 30 '14 at 21:18
  • @Alan--I think you have anticipated my "for another thread" musing at the end of MY "answer" to my question (below)! This is another place I've not been before--`setActionCommand`, so THANK YOU! This is a great tip! – DSlomer64 May 31 '14 at 02:21
  • I'll take that back--I've used `setActionCommand` before, just not in its proper context--as a workaround borne from ignorance: [here](http://stackoverflow.com/questions/19391869/whats-the-purpose-of-jtextfield-setactioncommand-how-do-you-access-it-progra/23898578#23898578) – DSlomer64 May 31 '14 at 02:22

5 Answers5

3

Generally any time you have repeated functionality like that, you want to extract that code out into a helper method like this:

private JButton makeJButton(String label, String toolTip, Color bgColor, final Color highlight) {
    JButton button = new JButton(label);
    button.setToolTipText(toolTip);
    button.setBackground(bgColor);
    button.setHorizontalAlignment(JButton.CENTER);
    button.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            Highlighter.shadeSymmetric(currentCell, highlight);
        }
    });
    return button;
}

Then your makeScoremasterBonuses() method becomes much simpler:

private void makeScoremasterBonuses() {
    pnlBonuses = new JPanel(new GridLayout(1, 6));
    pnlBonuses.setSize(6, 1);

    pnlBonuses.add(new JLabel("Legend: "));
    pnlBonuses.add(makeJButton("3W", "This is a triple-word cell.", TRIPLE_WORD, TRIPLE_WORD));
    pnlBonuses.add(makeJButton("3L", "This is a triple-letter cell.", TRIPLE_LETTER, TRIPLE_LETTER));
    pnlBonuses.add(makeJButton("2W", "This is a double-word cell.", DOUBLE_WORD, DOUBLE_WORD));
    pnlBonuses.add(makeJButton("3L", "This is a double-letter cell.", DOUBLE_LETTER, DOUBLE_LETTER));
    pnlBonuses.add(makeJButton("", "No bonus.", WHITE, B_NORMAL));
}
azurefrog
  • 10,785
  • 7
  • 42
  • 56
  • +1 I like this better than my own answer since you don't need an intermediate class. – maerics May 30 '14 at 23:06
  • @azure--I like this, too. Thanks. Now I have THREE answers to choose "the best" of. Gonna be hard. I especially like the "Generally... " part of your answer as a good explanation. Maerics' one-sentence explanation, was really good, too. THESE are the kinds of answers we need more of here, IMHO. At least they work for me. – DSlomer64 May 31 '14 at 00:36
2

Identify the aspects that vary, collect them, and iterate over the collection.

Something like this (untested):

pnlBonuses = new JPanel(new GridLayout(1, 6));
pnlBonuses.setSize(6,1);
pnlBonuses.add(new JLabel("Legend: "));

// Create class "CellInfo" with constructor and getters for desired properties.
CellInfo cellInfos[] = {
  new CellInfo("3W", "This is a triple-word cell.",   TRIPLE_WORD),
  new CellInfo("3L", "This is a triple-letter cell.", TRIPLE_LETTER),
  // ...
};

// Add a button for each item described by the cellInfos.
for (CellInfo cellInfo : cellInfos) {
  Button b = new JButton(cellInfo.getLabel());
  b.setToolTipText(cellInfo.getToolTipText());
  b.setBackground(cellInfo.getBackground());
  b.setHorizontalAlignment(JButton.CENTER);
  b.addActionListener(new ActionListener() {
  @Override public void actionPerformed(ActionEvent e) {
    Highlighter.shadeSymmetric(currentCell, cellInfo.getBackground());
  }});
  pnlBonuses.add(b);
}

Note that you might need to create some "final" variables for placeholders for use in the inner anonymous class but the idea should work.

maerics
  • 151,642
  • 46
  • 269
  • 291
  • @maerics--I like what I see! I'll work on implementing something like that. THANKS! (I like it even better with the EDIT!) [Afterthought: I don't care which button gets pressed, just that it does what I want, so no array of buttons is necssary, unlike what I assumed.] – DSlomer64 May 30 '14 at 21:17
2

An enum could be your friend here. It's almost an array of methods:

static enum Btn {

    TripleWord("3W", "This is a triple word cell.", TRIPLE_WORD),
    TripleLetter("3L", "This is a triple letter cell.", TRIPLE_LETTER),
    DoubleWord("2W", "This is a double word cell.", DOUBLE_WORD),
    DoubleLetter("2L", "This is a double letter cell.", DOUBLE_LETTER),
    NoBonus("", "No bonus.", WHITE, B_NORMAL);
    final String label;
    final String tooltip;
    final Color color;
    final Color shade;

    Btn(String label, String tooltip, Color color, Color shade) {
        this.label = label;
        this.tooltip = tooltip;
        this.color = color;
        this.shade = shade;
    }

    Btn(String label, String tooltip, Color color) {
        this(label, tooltip, color, color);
    }

    public JButton asJButton() {
        JButton btn = (new JButton(label));
        btn.setToolTipText(tooltip);
        btn.setBackground(color);
        btn.setHorizontalAlignment(JButton.CENTER);
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Highlighter.shadeSymmetric(currentCell, shade);
            }
        });
        return btn;
    }
}

private void makeScoremasterBonuses() {
    int nBtns = Btn.values().length;
    JPanel pnlBonuses = new JPanel(new GridLayout(1, nBtns + 1));
    pnlBonuses.setSize(nBtns + 1, 1);
    pnlBonuses.add(new JLabel("Legend: "));
    for (Btn btn : Btn.values()) {
        pnlBonuses.add(btn.asJButton());
    }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • @OldC--thanks! I've been meaning to master `enum`--used it a time or two very superficially. In fact, I'd hoped to use it to avoid the "triple word" stuff twice since I sort of recall you can get the enum value printed, but I gave up too easily. I'll study your and all the other answers. I wish I could pick more than one as "the answer", but at least I've upvoted all. – DSlomer64 May 31 '14 at 00:32
  • @OldCurmudgeon--Your answer takes me to a much higher `enum` level than I've used it--which has been most superficially at best. With (what seems to me to be) all the bells, etc. I don't know if I should call what I'm seeing an "enum constructor", but that's a place I've not been, so thanks for opening the door. – DSlomer64 May 31 '14 at 00:42
  • @DSlomer64 - Notice also that we can use the number of enums in the list to control the size of the `GridLayout` and and the panel size. – OldCurmudgeon May 31 '14 at 15:38
  • @OldCurmudgeon--I am going to ask a related question (in another thread) fairly soon that will be about `enum` and `values()` and how (if possible) to use those values as text. But I want to play around with enum first and since I still haven't quite figured out how your code works, I'll probably use it as my starting-exploration point. – DSlomer64 May 31 '14 at 20:01
1

(I know I could have edited my previous answer, but this one's so different...)

Thanks to @OldCurmudgeon, I have come up with what I think is pretty good.

Here's "proof" (I may just leave each label and tooltip as is):

enter image description here

  public enum Colors {
    TRIPLE_WORD    (255, 220,  50), 
    TRIPLE_LETTER  (255, 255, 150), 
    DOUBLE_WORD    (  0, 255,   0), 
    DOUBLE_LETTER  (214, 245, 214),
    NOT_A_BONUS    (255, 255, 255);

    private final int red, green, blue;

    Colors(int r, int g, int b){
      this.red   = r;
      this.green = g;
      this.blue  = b;
     }

    public java.awt.Color background(Colors c){
      return new java.awt.Color(c.red, c.green, c.blue);
    }
  }

  private void makeScoremasterBonuses(){
    Colors c;
    Colors all   [] = Colors.values();
    String labels[] = new String[all.length];
    String abbrs [] = new String[all.length];

    JButton but;
    pnlBonuses = new JPanel();
    pnlBonuses.add(new JLabel("Legend:"));

    for (int i = 0; i < all.length; i++) {
      labels[i] = all[i].name().replace("_", " ").toLowerCase();
      abbrs [i] = abbreviate(all[i].name());
      c = Colors.values()[i];
      but = new JButton(abbrs[i]);
      but.setToolTipText(labels[i]);
      but.setBackground(c.background(c));
      but.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
      but.setActionCommand("" + i);
      but.addActionListener(this);
      pnlBonuses.add(but);
    }
  }
DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • 1
    Thank you, @OldCurmudgeon, for starting me on the way to the above code! All I did was Google 'enum constructor in java', which led me to [this link](http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html), which, merely by mimicking what I saw there along with your code, guided me to a whole new place. What a great day! – DSlomer64 Jun 01 '14 at 19:44
  • I really liked your enthusiasm in developing your knowledge while progressing to your improved solution (as you had asked for originally) and the overall interaction with others along the way. I think it's a best example of how a question to answer dialog teaches and inspires us rather than just receiving a bare bones answer (especially after reading through a series of *closed for various reasons* questions that, IMHO, had more relevance than many of the closers took time to consider). – Zhora Sep 06 '14 at 03:23
  • @Zhora--What a nice comment, especially about the closed questions. There is a lot of shortness of temper (impatience?), it seems, at SO and your post--especially being months after this thread ended--just shows that there is ample positivity around, too. Thank you. – DSlomer64 Sep 07 '14 at 19:47
0

=== THIS IS A MAJOR EDIT OF WHAT I POSTED AN HOUR AGO ===

I wanted to see if I could implement my own naive method. Here it is:

public class Game implements ActionListener{

  public Color [] backgrounds = {TRIPLE_WORD, TRIPLE_LETTER, 
                                 DOUBLE_WORD, DOUBLE_LETTER, B_NORMAL};

  private void makeScoremasterBonuses(){
    String[] labels = {"3W", "3L", "2W", "2L", "  "};
    JButton but;

    pnlBonuses = new JPanel();
    pnlBonuses.add(new JLabel("Legend:"));

    for (int i = 0; i < labels.length; i++) {
      char wt = labels[i].charAt(0);
      char tp = labels[i].charAt(1);
      but = new JButton(labels[i]);//("" + i);
      but.setBackground(backgrounds[i]);
      but.setHorizontalAlignment(SwingConstants.CENTER);
      but.setActionCommand("" + i);
      but.addActionListener(this);
      but.setToolTipText("This is a " 
          + (i == labels.length - 1 ? "non-bonus" :
                          (wt == '3' ? "triple" : "double") 
                  + " " + (tp == 'L' ? "letter" : "word")) 
          + " cell.");
      pnlBonuses.add(but);
    }    
  }

  public void actionPerformed(ActionEvent evt) {
    int i = Integer.parseInt(evt.getActionCommand());
    Highlighter.shadeSymmetric(currentCell,backgrounds[i]);
  }

This has NOW (after edits) EVEN MORE SO been the best thread I've initiated, in terms of quality of responses and all that I've learned because of them. THANK YOU ALL.

BUT I STILL haven't managed to appropriately use setActionCommand. Whatever I did to TRY to use it wound up being so much longer code-wise that I gave up and went for the short and easy but inappropriate.

Any thoughts about how to use set... and getActionCommand the right way (i.e., as Actions) without adding a ton of code to do so?

DSlomer64
  • 4,234
  • 4
  • 53
  • 88
  • Seeing `("3W", "This is a triple word cell.", TRIPLE_WORD)` and the like in EVERYbody's Answer made me think... "You know, those 3 pieces of info could possibly be derived from ONE piece--this `enum`: `private enum Actions { TRIPLE_WORD, TRIPLE_LETTER, DOUBLE_WORD, DOUBLE_LETTER, NOT_A_BONUS }`. I don't know whether to start another thread or EDIT my own Answer and pose the question there. I'll wait until somebody advises, I guess. – DSlomer64 May 31 '14 at 21:43
  • YEAH I AM! SURE! ABOUT WHERE I WAS GOING! – DSlomer64 May 31 '14 at 23:42