8皇后已经是一个很古老的问题了,今天我用OSGI方式整合一下8皇后。
工具:eclipse
第一步:分析需求,每个人的思想都不一样。对OSGI的理解也不一样,我理解为把8皇后问题分成8个模块
1、棋盘
2、皇后
3、自定义异常
4、打印接口
5、JAVA控制台方式打印
6、Swing方式打印
7、显示工厂(根据需求不同生成5或6方式对象)
8、启动
以下是我建立的项目
棋盘类:
package chessboard; import java.util.HashSet; import java.util.Set; import chessman.Chessman; import exception.ChessmanOutIndexException; //棋盘 public class Chessboard { // 棋盘最大数量 private final int gridMaxNum; // 棋子 private Set<Chessman> chessmans; // 初始化 public Chessboard(int gridMaxNum) { this.gridMaxNum = gridMaxNum; chessmans = new HashSet<Chessman>(); } public int getGridMaxNum() { return gridMaxNum; } // 给棋盘添加棋子 public void addChessman(Chessman chessman) { if (chessmans.size() >= this.getGridMaxNum()) { throw new ChessmanOutIndexException("棋子个数已经超出棋盘当前最大值:" + chessmans.size() + "/" + this.getGridMaxNum()); } chessmans.add(chessman); } // 移除棋盘中的棋子 public void removeChessman(Chessman chessman) { if (chessmans.isEmpty()) { throw new ChessmanOutIndexException("期盘中已经没有棋子,不能在移除棋子:" + chessmans.size() + "/" + this.getGridMaxNum()); } chessmans.remove(chessman); } public boolean existEating() { for (Chessman chessman1 : chessmans) { for (Chessman chessman2 : chessmans) { if ((!chessman1.equals(chessman2)) && chessman1.isEating(chessman2)) { return true; } } } return false; } public static void main(String[] args) { HashSet<Chessman> set = new HashSet<Chessman>(); Chessman empress = Chessman.createEmpress(); empress.setRow(1); empress.setColumn(2); set.add(empress); empress.setColumn(3); set.remove(empress); System.out.println(set); } }
棋盘引用:
皇后类:
package chessman; public abstract class Chessman { /** * 表示此棋子在棋盘中第几行 第几列 */ //行 private int row; //列 private int column; public int getRow() { return row; } public void setRow(int row) { this.row = row; } public int getColumn() { return column; } public void setColumn(int column) { this.column = column; } @Override public String toString() { return "["+row+","+column+"]"; } //检测是否可以放置棋子 public abstract boolean isEating(Chessman chessman); //生成棋子 public static Chessman createEmpress(){ return new Empress(); } //私有的内部类,并继承棋子类,实现是否可放棋子的抽象方法 private static class Empress extends Chessman{ @Override public boolean isEating(Chessman chessman) { if(chessman.getRow() == this.getRow()){//同行 return true; }else if(chessman.getColumn() == this.getColumn()){//同列 return true; }else if(chessman.getColumn() - chessman.getRow() == this.getColumn() - this.getRow()){//正斜线 return true; }else if(chessman.getColumn() + chessman.getRow() == this.getColumn() + this.getRow()){//反斜线 return true; } return false; } } }
皇后类无引用
显示接口:
package view; import java.util.List; //显示的接口 public interface IView { public int readGridNum(); public void showResult(List<int[]> result); }
接口无引用
控制台方式显示:
package consoleview; import java.io.BufferedReader; import java.io.Console; import java.io.IOException; import java.io.InputStreamReader; import java.util.Arrays; import java.util.List; import view.IView; //打印 public class ConsoleView implements IView { private Console console = System.console(); public String readLine(String msg) throws IOException { if (console != null) { return console.readLine(msg); } else { System.out.print(msg); InputStreamReader isr = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(isr); return br.readLine(); } } public void println(String msg) { System.out.println(msg); } @Override public int readGridNum() { do { try { String line = this.readLine("请输入皇后个数(大于0, 输入EXIT退出):"); if (line.trim().equalsIgnoreCase("exit")) { System.exit(0); } else { int num = Integer.parseInt(line); if (num <= 0) { throw new Exception(); } return num; } } catch (IOException e) { this.println("系统出现IO异常"); e.printStackTrace(); System.exit(0); } catch (Exception e) { this.println("输入的值非法,必须是大于0的整数"); } } while (true); } @Override public void showResult(List<int[]> results) { if (results == null || results.isEmpty()) { this.println("无解"); return; } do { try { String line = this.readLine("共有[" + results.size() + "]个解,请输入数字查看具体解的答案(1-" + results.size() + ", 输入EXIT退出):"); if (line.trim().equalsIgnoreCase("exit")) { System.exit(0); return; } else { int num = Integer.parseInt(line) - 1; if (num < 0 || num >= results.size()) { throw new Exception(); } int[] result = results.get(num); this.println("第" + (num + 1) + "个解为" + Arrays.toString(result) + ".下面是该解的图形"); this.printMap(result); } } catch (IOException e) { this.println("系统出现IO异常"); e.printStackTrace(); System.exit(0); } catch (Exception e) { this.println("输入的值非法,必须是大于1-" + results.size() + "的整数"); } } while (true); } // 打印的主函数 public void printMap(int[] result) { this.printMapLine(result.length); for (int i = 0; i < result.length; i++) { this.printMapGrid(result.length, result[i]); this.printMapLine(result.length); } } // 打印棋子与棋盘 private void printMapGrid(int length, int empressX) { StringBuffer sb = new StringBuffer(); sb.append("|"); for (int i = 0; i < length; i++) { sb.append(i == empressX ? "X|" : " |"); } this.println(sb.toString()); } // 打印棋盘边界及棋盘中间线 private void printMapLine(int length) { StringBuffer sb = new StringBuffer(); sb.append("+"); for (int i = 0; i < length; i++) { sb.append("-+"); } this.println(sb.toString()); } }
控制台方式显示引用
swing方式显示
类1:
package frameview; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.List; import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.UIManager; import view.IView; public class FrameView extends JPanel implements IView{ private static final long serialVersionUID = -7997619554458231450L; private JFrame frame; //主窗体 private JDialog dialog; //输入对话框 private JPanel inputPanel; //输入对话框的布局面板 private JNumberField field; //自定义的数字输入框 private JPanel outputPanel; //输出布局面板 private JComboBox result; //所有解的下拉选择框 private JPanel map; //棋盘面板 //初始化,构造器 public FrameView(){ //选择外观 String lookandfeel="com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel"; try { UIManager.setLookAndFeel(lookandfeel); } catch (Exception e) { e.printStackTrace(); } //创建主窗体,并设置相应属性 frame = new JFrame(); frame.setTitle("N皇后问题"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); createInputPanel(); createOutputPanel(); } /** * 创建输出面板,就是画期盼的哪个 */ private void createOutputPanel(){ outputPanel = new JPanel();//创建面板 outputPanel.setLayout(new BorderLayout()); //设置布局为边框布局 JPanel toppanel = new JPanel();//创建顶部面板 outputPanel.add(toppanel, BorderLayout.NORTH);//放在输出面板的顶部 result = new JComboBox();//创建下拉框 result.addItemListener(new ItemListener() {//注册下拉框监听器,当选项变化的时候调用 @Override public void itemStateChanged(ItemEvent e) { if(e.getStateChange() == ItemEvent.SELECTED){//如果选项的变化状态是选中的 Result item = (Result)e.getItem();//获取选项内容 int[] result = item.getResult();//获取坐标 showMap(result);//输出棋盘 } } }); //设置下拉框不可编辑 result.setEditable(false); toppanel.add(result); //将下拉框放入顶部面板 map = new JPanel();//创建棋盘面板并放入中部位置 outputPanel.add(map); frame.add(outputPanel); } /** * 创建输入面板 */ private void createInputPanel(){ inputPanel = new JPanel(); //标签 JLabel label = new JLabel("皇后个数:"); inputPanel.add(label); //数字输入框,设施数字至少是Integer类型 field = new JNumberField(Integer.class); field.setMinValue(1);//允许输入的最小值 field.setPreferredSize(new Dimension(100, 30)); inputPanel.add(field); //按钮,设置监听动作,点击后关闭对话框 JButton button = new JButton("确定"); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { dialog.dispose(); dialog = null; } }); inputPanel.add(button); } @Override public int readGridNum() {//返回输入的结果 this.showInputDialog();//现实输入框,注意对话框是模式对话框,因此会阻塞在这里,直至该对话框关闭掉,在继续运行 String text = field.getText();//获取数字框的值,并返回 return Integer.parseInt(text); } @Override public void showResult(List<int[]> results) {//显示所有的结果 for(int i=0;i<results.size();i++){//将结果放入下拉列表 int[] result = results.get(i); Result rs = new Result(result, i); this.result.addItem(rs); } if(this.result.getItemCount() > 0){//如果有解默认选择第一个 this.result.setSelectedIndex(0); }else{//如果无解,打印无解 map.removeAll(); map.setLayout(new BorderLayout()); map.setAlignmentX(JPanel.CENTER_ALIGNMENT); map.setAlignmentY(JPanel.CENTER_ALIGNMENT); JLabel nowap = new JLabel("无解"); map.add(nowap); } frame.pack();//窗体自适应面板布局大小 frame.setLocationRelativeTo(null);//窗体现实在屏幕中央 frame.setResizable(false);//不允许窗体大小可变 frame.setVisible(true);//显示窗体 } private void showInputDialog(){ //创建对话框,病设置变体,模式,注册监听,放入面板 dialog = new JDialog(frame, "N皇后问题", true); dialog.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); dialog.add(inputPanel); dialog.setResizable(false);//窗体大小不可变 dialog.pack();//大小自适应面板大小 dialog.setLocationRelativeTo(null);//位置居中 dialog.setVisible(true);//显示,由于JDialog(frame, "N皇后问题", true); 模式参数为true,因此这句话一执行变阻塞到这里,直至窗体关闭继续运行 } private void showMap(int[] data){ //面板移除所有的东西 map.removeAll(); //设置布局管理器 map.setLayout(new GridLayout(data.length, data.length)); for(int i=0;i<data.length;i++){ for(int j=0;j<data.length;j++){ JLabel grid = new JLabel();//创建格子 grid.setOpaque(true);//背景不透明 if(data[i] == j){ grid.setBackground(Color.RED);//红色 }else{ grid.setBackground(Color.BLUE);//蓝色 } grid.setBorder(BorderFactory.createLineBorder(Color.BLACK));//格子边框 grid.setPreferredSize(new Dimension(20,20));//各自默认大小 map.add(grid); } } //重新绘制 frame.repaint(); frame.validate(); } private class Result{ private int[] result; private int index; public Result(int[] result, int index){ this.result = result; this.index = index; } public int[] getResult() { return result; } @Override public String toString() {// 当该对象作为元素放入下拉列表后,下拉列表会调用对象toString方法,将其返回值打印在下拉面板上 return "解" + (index + 1); } } }
类2:package frameview; import java.awt.Toolkit; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.swing.JTextField; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.PlainDocument; public class JNumberField extends JTextField{ private static final long serialVersionUID = 2711657283309257375L; private Class<? extends Number> type; private double minValue = Double.NEGATIVE_INFINITY; private double maxValue = Double.POSITIVE_INFINITY; public JNumberField(Class<? extends Number> type){ this.type = type; this.setHorizontalAlignment(JTextField.RIGHT); } public double getMinValue() { return minValue; } public void setMinValue(double minValue) { this.minValue = minValue; } public double getMaxValue() { return maxValue; } public void setMaxValue(double maxValue) { this.maxValue = maxValue; } @Override protected Document createDefaultModel() { return new PlainDocument(){ private static final long serialVersionUID = -4589029521250625953L; @Override public void insertString(int offs, String str, AttributeSet a) throws BadLocationException { if(str == null) return; int length = getLength(); String value = getText(0, length); value = value.substring(0, offs) + str + value.substring(offs, length); try{ Number number = parser(value); if(number.doubleValue() < minValue || number.doubleValue() > maxValue){ throw new Exception(); } }catch(Exception e){ Toolkit.getDefaultToolkit().beep(); return; } super.insertString(offs, str, a); } }; } private Number parser(String value) throws SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException { String name = type.getSimpleName(); if (type == Integer.class) { name = "Int"; } Method method = type.getMethod("parse" + name, String.class); Object result = method.invoke(null, value); return type.cast(result); } }
实现接口:
显示工厂:
package viewfactory; import view.IView; import consoleview.ConsoleView; import exception.NullViewTypeException; import frameview.FrameView; public class ViewFactory { public static enum ViewType{ FRAME("窗体View"), CONSOLE("控制台View"); private String value; private ViewType(String value){ this.value = value; } @Override public String toString() { return this.value; } } public static IView createView(ViewType type){ switch(type){ case FRAME: return new FrameView(); case CONSOLE: return new ConsoleView(); default: throw new NullViewTypeException("无法识别类型:" + type.toString()); } } }
显示工厂引用:
自定义异常类:
类1:
package exception; //声明异常类 public class ChessmanOutIndexException extends RuntimeException{ private static final long serialVersionUID = 1668411926899207995L; public ChessmanOutIndexException(){ super(); } public ChessmanOutIndexException(String msg){ super(msg); } public ChessmanOutIndexException(String msg, Throwable t){ super(msg, t); } }
类2:package exception; public class NullViewTypeException extends RuntimeException{ private static final long serialVersionUID = 8231353296327423242L; public NullViewTypeException(){ super(); } public NullViewTypeException(String msg){ super(msg); } public NullViewTypeException(String msg, Throwable t){ super(msg, t); } }
自定义异常类无引用
启动类:
package start; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import view.IView; import viewfactory.ViewFactory; import viewfactory.ViewFactory.ViewType; import chessboard.Chessboard; import chessman.Chessman; public class NEmpressQuestion { private Chessman[] chessmans; private Chessboard chessboard; private Set<int[]> answers; public NEmpressQuestion(int empressCount){ chessmans = new Chessman[empressCount]; for(int i=0;i<chessmans.length;i++){ chessmans[i] = Chessman.createEmpress(); chessmans[i].setRow(i); } chessboard = new Chessboard(empressCount); answers = new HashSet<int[]>(); } public void running(){ this.nextRow(0); } private void nextRow(int row){ chessboard.addChessman(chessmans[row]); for(int col=0;col<chessboard.getGridMaxNum();col++){ chessmans[row].setColumn(col); if(!chessboard.existEating()){ if(row<chessboard.getGridMaxNum() - 1){ nextRow(row + 1); }else{ recordAnswer(); } } } chessboard.removeChessman(chessmans[row]); } public void recordAnswer(){ int[] answer = new int[chessmans.length]; for(int i=0;i<chessmans.length;i++){ answer[i] = chessmans[i].getColumn(); } answers.add(answer); } public Set<int[]> getAnswers(){ return answers; } //程序的主函数入口 public static void main(String[] args) { ViewType type=ViewType.CONSOLE; if(args != null && args.length > 0 && args[0].trim().equalsIgnoreCase("frame")){ type = ViewType.FRAME; } IView view = ViewFactory.createView(type); int gridNum = view.readGridNum(); NEmpressQuestion nempress = new NEmpressQuestion(gridNum); nempress.running(); Set<int[]> answers = nempress.getAnswers(); List<int[]> results = new ArrayList<int[]>(answers.size()); results.addAll(answers); view.showResult(results); } }
启动类引用:
注意启动类的
ViewType type=ViewType.CONSOLE; 这一行,这一行就是具体需要哪个显示方式了,这里是控制台方式