经典软件体系结构风格(二):层次软件体系结构。例程代码优化!

实验内容截取

实验目的

(1)理解基于事件的隐式调用软件体系结构、层次软件体系结构的原理

(2)掌握事件的隐式调用软件体系结构、层次软件体系结构的实例

(3)事件的隐式调用软件体系结构、层次软件体系结构的编程实现

实现内容

2.层次软件体系结构

基于层次软件体系结构的软件测试系统。

第一层为用户图形界面层

public class TestingGUI extends JPanel {
	private JTextArea txtTestInfo, txtTestcase;
	private JLabel lblTestcases;
	private JPanel buttonPanel;
	private JComboBox cmbTestcases;

	private static final String CASE_BUBBLE = "TC1-Test Bubble Sort";
	private static final String CASE_HEAP = "TC2-Test Heap Sort";
	private static final String CASE_INSERTION = "TC3-Test Insertion Sort";
	private static final String EXECUTE = "Execute";
	private static final String EXIT = "Exit";

	public TestingGUI() {
		txtTestInfo = new JTextArea("Test output from source shown here\n", 6, 20);
		txtTestInfo.setLineWrap(true);
		txtTestcase = new JTextArea("Testcase info and test validation shown here\n", 4, 15);
		txtTestcase.setLineWrap(true);
		buildUpScrollGUI();
	}

	private void buildUpScrollGUI() {
		setUpButtonPanel();
		JScrollPane btnPane = new JScrollPane(buttonPanel);
		JScrollPane textPane = new JScrollPane(txtTestcase);
		textPane.setMinimumSize(new Dimension(250, 150));
		JScrollPane testDataPane = new JScrollPane(txtTestInfo);

		JSplitPane upSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		upSplitPane.setLeftComponent(btnPane);
		upSplitPane.setRightComponent(testDataPane);
		JScrollPane downPane = new JScrollPane(textPane);

		Dimension minimumSize = new Dimension(130, 100);
		btnPane.setMinimumSize(minimumSize);
		textPane.setMinimumSize(new Dimension(100, 100));
		upSplitPane.setDividerLocation(270);
		upSplitPane.setPreferredSize(new Dimension(500, 300));

		JSplitPane bigSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, upSplitPane, downPane);
		bigSplitPane.setDividerLocation(190);

		add(bigSplitPane);
		setSize(new Dimension(500, 400));
		setVisible(true);
	}
    
    // ......
    // ......
    // 略 
}

还有其他一些代码,我这里就不放出来了。。。

效果图和功能

选择排序算法,点击Execute,会随机生成指定长度的序列。并使用指定的排序算法进行排序。输出排序后的序列,排序耗时等信息。

这是软件体系结构这门课程的一个实验作业。给出例程的目的原本是让我们调试程序,从而理解程序隐含的软件体系结构。😂我比较强迫症,看着这多多少少有点毛病的代码,想把它改成自己满意的样子。

例程的问题

(1)每一个Testcase的实现类的代码存在大量的重复

(2)随机生成序列、排序、校验排序结果3个操作都是耗时操作,然而这3个操作都以同步的方式放在主线程中。当数据量较大,经我测试,当数组元素个数为10万时,点击一次执行,会造成较长时间的阻塞。当快速多次点击执行,程序崩溃,界面卡住不动。

优化方案

(1)将重复代码抽离出来放到父类,只需要将实例化算法对象的操作交由子类实现即可。

(2)每一次execute操作都有独立的线程异步操作。然而这时会遇到两个问题:

①异步线程何时返回结果,返回的结果如何更新到界面?

   通过事件回调的方式,解决异步线程的结果返回。

②当多次点击execute后,多个线程异步操作。当某个线程已经完成,从而回调到界面,正在在界面渲染数据,此时又一个线程完成,同时又向界面渲染数据。界面的耗时渲染,让我们看到了多线程并发的界面数据错乱现象。

   只需要将渲染界面的操作放在同步代码块之内即可

修改后的代码

我只贴出我有更改的类的代码。

Testcase

package demo2.test;

public interface Testcase {
	
	void execute(int len, EventCallback callback);
	
	interface EventCallback {
		/**
		 * 包含了3个操作:生成指定长度的随机数列,排序,校验排序结果是否正确
		 * @param sortedArr		排序好的数组
		 * @param costTime		排序所花费的时间(不包括生成随机数列的时间和校验排序结果的时间)
		 * @param isCorrect		排序结果是否正确
		 */
		void onExecuted(int[] sortedArr, long costTime, boolean isCorrect);
	}
}

AlgorithmTestCase

package demo2.test;

import demo2.IntegerArrGenerator;
import demo2.ResultVerification;
import demo2.algorithm.SortAlgorithm;

/**
 * Testcase接口的实现类,但是个抽象类。
 * 实例化算法对象的操作通过抽象方法抛给子类实现
 * 
 * @author	passerbyYSQ
 * @date	2020-9-15 20:47:09
 */
public abstract class AlgorithmTestCase<T extends SortAlgorithm> implements Testcase {
	
	protected T algorithm;
	protected Context context;
	
	protected abstract T crateAlgorithm();
	
	@Override
	public void execute(int len, EventCallback callback) {
		Thread th = new Thread(new GenerateAndSortInt(len, callback));
		th.start();
	}

	class GenerateAndSortInt implements Runnable {
		long startTime;
		long timeTaken = 0;
		int len;
		int[] arr;
		EventCallback callback;
		
		public GenerateAndSortInt(int len, EventCallback callback) {
			this.len = len;
			this.callback = callback;
		}
		
		@Override
		public void run() {
			// 耗时
			arr = IntegerArrGenerator.generateInput(len);

			algorithm = crateAlgorithm();
			context = new Context(algorithm);
			
			// 排序。耗时
			startTime = System.currentTimeMillis();
			arr = context.sortIntArray(arr);
			long costTime = System.currentTimeMillis() - startTime;
			
			// 校验排序结果。耗时
			boolean isCorrect = ResultVerification.isResultCorrect(arr);
			
			callback.onExecuted(arr, costTime, isCorrect);
		}
	}

}

TestcaseBubble

package demo2.test;

import demo2.algorithm.BubbleSort;

public class TestcaseBubble extends AlgorithmTestCase<BubbleSort> {

	@Override
	protected BubbleSort crateAlgorithm() {
		return new BubbleSort();
	}
	
}

TestcaseHeap

package demo2.test;

import demo2.algorithm.HeapSort;

public class TestcaseHeap extends AlgorithmTestCase<HeapSort> {

	@Override
	protected HeapSort crateAlgorithm() {
		return new HeapSort();
	}

}

TestcaseInsertion

package demo2.test;

import demo2.algorithm.InsertSort;

public class TestcaseInsertion extends AlgorithmTestCase<InsertSort> {

	@Override
	protected InsertSort crateAlgorithm() {
		return new InsertSort();
	}

}

TestingGUI

package demo2;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;

import demo2.test.Testcase;
import demo2.test.Testcase.EventCallback;
import demo2.test.TestcaseBubble;
import demo2.test.TestcaseHeap;
import demo2.test.TestcaseInsertion;

public class TestingGUI extends JPanel {
	private JTextArea txtTestInfo, txtTestcase;
	private JLabel lblTestcases;
	private JPanel buttonPanel;
	private JComboBox cmbTestcases;

	private static final String CASE_BUBBLE = "TC1-Test Bubble Sort";
	private static final String CASE_HEAP = "TC2-Test Heap Sort";
	private static final String CASE_INSERTION = "TC3-Test Insertion Sort";
	private static final String EXECUTE = "Execute";
	private static final String EXIT = "Exit";

	public TestingGUI() {
		txtTestInfo = new JTextArea("Test output from source shown here\n", 6, 20);
		txtTestInfo.setLineWrap(true);
		txtTestcase = new JTextArea("Testcase info and test validation shown here\n", 4, 15);
		txtTestcase.setLineWrap(true);
		buildUpScrollGUI();
	}

	private void buildUpScrollGUI() {
		setUpButtonPanel();
		JScrollPane btnPane = new JScrollPane(buttonPanel);
		JScrollPane textPane = new JScrollPane(txtTestcase);
		textPane.setMinimumSize(new Dimension(250, 150));
		JScrollPane testDataPane = new JScrollPane(txtTestInfo);

		JSplitPane upSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		upSplitPane.setLeftComponent(btnPane);
		upSplitPane.setRightComponent(testDataPane);
		JScrollPane downPane = new JScrollPane(textPane);

		Dimension minimumSize = new Dimension(130, 100);
		btnPane.setMinimumSize(minimumSize);
		textPane.setMinimumSize(new Dimension(100, 100));
		upSplitPane.setDividerLocation(270);
		upSplitPane.setPreferredSize(new Dimension(500, 300));

		JSplitPane bigSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, upSplitPane, downPane);
		bigSplitPane.setDividerLocation(190);

		add(bigSplitPane);
		setSize(new Dimension(500, 400));
		setVisible(true);
	}

	private void setUpButtonPanel() {
		lblTestcases = new JLabel("Test Cases:");
		cmbTestcases = new JComboBox();
		cmbTestcases.addItem(CASE_BUBBLE);
		cmbTestcases.addItem(CASE_HEAP);
		cmbTestcases.addItem(CASE_INSERTION);

		// Create the open button
		JButton executeBtn = new JButton(EXECUTE);
		executeBtn.setMnemonic(KeyEvent.VK_S);
		JButton exitButton = new JButton(EXIT);
		exitButton.setMnemonic(KeyEvent.VK_X);

		BtnListener objButtonHandler = new BtnListener();
		// add action Listener
		executeBtn.addActionListener(objButtonHandler);
		exitButton.addActionListener(objButtonHandler);
		buttonPanel = new JPanel();

		GridBagLayout gridbag = new GridBagLayout();
		buttonPanel.setLayout(gridbag);
		GridBagConstraints gbc = new GridBagConstraints();

		buttonPanel.add(lblTestcases);
		buttonPanel.add(cmbTestcases);
		buttonPanel.add(executeBtn);
		buttonPanel.add(exitButton);
		gbc.insets.top = 5;
		gbc.insets.bottom = 5;
		gbc.insets.left = 5;
		gbc.insets.right = 5;

		gbc.anchor = GridBagConstraints.EAST;
		gbc.gridx = 0;
		gbc.gridy = 0;
		gridbag.setConstraints(lblTestcases, gbc);
		gbc.anchor = GridBagConstraints.WEST;
		gbc.gridx = 1;
		gbc.gridy = 0;
		gridbag.setConstraints(cmbTestcases, gbc);
		gbc.anchor = GridBagConstraints.EAST;
		gbc.insets.left = 2;
		gbc.insets.right = 2;
		gbc.insets.top = 25;
		gbc.anchor = GridBagConstraints.EAST;
		gbc.gridx = 0;
		gbc.gridy = 7;
		gridbag.setConstraints(executeBtn, gbc);
		gbc.anchor = GridBagConstraints.WEST;
		gbc.gridx = 1;
		gbc.gridy = 7;
		gridbag.setConstraints(exitButton, gbc);
	}

	public void showTestInfo(int[] str) {
		txtTestInfo.setText("");
		for (int n = 0; n < str.length; n++)
			txtTestInfo.append("" + str[n] + " ");
//		StringBuilder sbd = new StringBuilder("");
//		for (int n = 0; n < str.length; n++) {
//			sbd.append(str[n]).append(" ");
//		}
//		txtTestInfo.setText(sbd.toString());	
	}

	public void showText(String err) {
		txtTestcase.append(err + "\n");
	}

	public String getSelectedTestcase() {
		return (String) cmbTestcases.getSelectedItem();
	}

	/**
	 * 实现了EventCallback接口。当异步线程执行完成,会回调到这里的 onExecuted()方法
	 * @author	passerbyYSQ
	 * @date	2020-9-15 20:56:02
	 */
	class BtnListener implements ActionListener, EventCallback {
		private Testcase test;
		private String selectedTestcase;

		public void actionPerformed(ActionEvent e) {
			

			if (e.getActionCommand().equals(EXIT)) {
				System.exit(1);
			}
			if (e.getActionCommand().equals(EXECUTE)) {
				selectedTestcase = getSelectedTestcase();
				
				if (selectedTestcase.equals(CASE_BUBBLE))
					test = new TestcaseBubble();
				else if (selectedTestcase.equals(CASE_HEAP))
					test = new TestcaseHeap();
				else if (selectedTestcase.equals(CASE_INSERTION))
					test = new TestcaseInsertion();
				
				test.execute(100000, this);
			}
		}

		/**
		 * 多线程并发问题:
		 * 假如频繁操作,导致多个数组陆续排序好。前一个数组还没操作完界面,另一个数组
		 * 又开始更新界面。会导致界面的数组输出错乱。
		 *所以操作ui的方法需要加上synchronized限定
		 */
		@Override
		public synchronized void onExecuted(int[] sortedArr, long costTime, boolean isCorrect) {
			// 更新ui最好也不要使用主线程。
			int[] output = sortedArr;
			showTestInfo(output);
			showText(selectedTestcase);
			// 结果检验也是个费时操作,不应该放到ui更新里面做。否则会出现卡顿现象
			showText("No Error found = " + isCorrect);
			showText("Testing Time takes = " + costTime + "\n");
		}
	}

	private static void createAndShowGUI() {
		JFrame.setDefaultLookAndFeelDecorated(true);
		JFrame frame = new JFrame("Layered Architecture- Software Testing");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		TestingGUI newContentPane = new TestingGUI();
		newContentPane.setOpaque(true);
		frame.setContentPane(newContentPane);
		frame.pack();
		frame.setVisible(true);
	}

	public static void main(String argv[]) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});
	}

}

  • 6
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值