• 尽可能的降低重复劳动——模板策略


    有感于 XCode 代码模板机制,写了一个小工具,特此写博文以志。

    模板的概念很简单,就是能拿来重复使用的东西。

    在编程语言的范畴里,就是一份源代码文件,

    里面有一些特定的字符串你给替换成其他的字符串以后,

    边成为了一份新的、具有实际运用价值的代码文件。

    代码模板文件仅仅是一具空壳子,

    要让它真正的活起来,就要在里面插入具有实际操作效果的方法、逻辑。

    我用 Java,一个很常用的模式就是:

    将待处理的文件拽入 GUI 获取文件的路径,然后对该文件进行处理。

    我觉得这样很方便,以前刚开始弄 java swing 的时候,

    选个文件还得打开一个 JFileChooser 文件选择对话框,那是各种麻烦和蛋疼,

    程序编写方面没有 “拖拽取文件路径” 简洁,实际使用起来也没有后者简洁~

    这么一来,完全可以抛弃  JFileChooser 这个控件了,谁爱用谁用去,反正我是不会再用了~

    另外,程序 ui 重用,派生子类重写父类的某些方法以扩展功能等,

    都可以用模板机制来降低书写重复代码的工作量。

    用实际源码来举例子吧:

    Gui.java

    package org.bruce.vertices.asist.common;
    
    import java.awt.Toolkit;
    import java.awt.dnd.DnDConstants;
    import java.awt.dnd.DropTarget;
    
    import javax.swing.JFrame;
    import javax.swing.JScrollBar;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    
    
    /**
     * @author Bruce Yang
     * 程序 ui~
     */
    public class Gui extends JFrame {
    	private static final long serialVersionUID = -7044615012246731094L;
    	
    	public static final int JFRAME_WIDTH = 500;
    	public static final int JFRAME_HEIGHT = 400;
    	
    	/**
    	 * @param title	“单元处理器”的名称
    	 * @param dtl	DropTargetListener 实例~
    	 */
    	public Gui(String title, Dta dtl) {
    		this.setTitle(title);
    		
    		this.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);
    		JScrollPane jsp = new JScrollPane();
    		JTextArea jta = new JTextArea();
    		jsp.setViewportView(jta);
    		this.add(jsp);
    		JScrollBar jsb = jsp.getVerticalScrollBar();
    		jsb.setValue(jsb.getMaximum()); 
    		
    		int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
    		int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
    		
    		this.setLocation((screenWidth-JFRAME_WIDTH)/2, (screenHeight-JFRAME_HEIGHT)/2);
    		
    		/** 关闭的时候将修改过的属性覆盖到 jar 里面的属性配置文件~ */
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		new DropTarget(jta, DnDConstants.ACTION_COPY_OR_MOVE, dtl);
    		this.setVisible(true);
    	}
    }
    Gui.java 是 “Drag && Drop” 类工具程序的界面窗口

    接收两个参数,一个是窗口的标题栏文字,另一个是一个 DropTargetListener 的实现对象~

    标题栏文字的作用在于提示用户该窗口用来完成什么功能,

    实现 DropTargetListener  接口的对象封装了对拽入文件进行如何处理的逻辑。

    Dta.java

    package org.bruce.vertices.asist.common;
    
    import java.awt.datatransfer.DataFlavor;
    import java.awt.dnd.DnDConstants;
    import java.awt.dnd.DropTargetAdapter;
    import java.awt.dnd.DropTargetDropEvent;
    import java.io.File;
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    import org.bruce.vertices.asist.utils.Functions;
    
    /**
     * @author Bruce Yang
     * 拖拽监听~
     */
    public abstract class Dta extends DropTargetAdapter {
    	
    	/**
    	 * @param f
    	 */
    	protected abstract void handle(File f);
    	
    	/* (non-Javadoc)
    	 * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent)
    	 */
    	@SuppressWarnings("unchecked")
    	public void drop(DropTargetDropEvent event) {
    		if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
    			event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
    			
    			DataFlavor df = DataFlavor.javaFileListFlavor;
    			List<File> list = null;
    			try {
    				list = (List<File>)(event.getTransferable().getTransferData(df));
    			} catch (Exception e) {
    				e.printStackTrace();
    			}
    			
    			Iterator<File> iterator = list.iterator();
    			while (iterator.hasNext()) {
    				File item = iterator.next();
    				
    				List<File> fileList = new ArrayList<File>();
    				if(item.isDirectory()) {
    					Functions.listFilesOnly(item, fileList);
    				} else {
    					fileList.add(item);
    				}
    				
    				for(File f : fileList) {
    					handle(f);
    				}
    			}
    			event.dropComplete(true);
    		} else {
    			event.rejectDrop();
    		}
    	}
    }
    Dta.java 是 DropTargetAdapter 类的子类,用来为 Gui 类的 JTextArea 控件增加拖拽取文件路径的功能。

    同时,Dta 是一个抽象类,它包含了一个 handle() 抽象方法,这个方法将留给 Dta 的子类来重写。

    至此,也就到了本文的重点内容了。

    Dta 的子类只要重写了 handle() 抽象方法,就能够实现不同的文件处理功能。

    下面给出几个例子:

    1。包含打印出拽入文件(可以是多个文件)路径功能的子类实现:

    PrintAbsPath.java

    package org.bruce.vertices.asist.units;
    
    import java.io.File;
    
    import org.bruce.vertices.asist.common.Dta;
    import org.bruce.vertices.asist.common.Gui;
    
    /**
     * @author Bruce Yang
     * 这么一来就清晰多了~
     */
    public class PrintAbsPath extends Dta {
    
    	@Override
    	public void handle(File f) {
    		// TODO Auto-generated method stub
    		System.out.println(f.getAbsolutePath());
    	}
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		new Gui("PrintAbsPath", new PrintAbsPath());
    	}
    }

    2。改变拽入文件中字符编码格式的子类实现

    ConvertEncoding.java

    package org.bruce.vertices.asist.units;
    
    import java.io.File;
    
    import org.bruce.vertices.asist.common.Dta;
    import org.bruce.vertices.asist.common.Gui;
    import org.bruce.vertices.asist.utils.StringFileBridge;
    
    /**
     * @author Bruce Yang
     * 改变文件的字符编码(GBK -> UTF-8)~
     */
    public class ConvertEncoding extends Dta {
    	private static final String FROM = "GBK";
    	private static final String TO = "UTF-8";
    
    	@Override
    	protected void handle(File f) {
    		// TODO Auto-generated method stub
    		String rawStr = StringFileBridge.file2String(f, FROM);
    		String convertedStr = StringFileBridge.changeEncode(rawStr, TO);
    		
    		StringFileBridge.string2File(convertedStr, f);
    	}
    	
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		new Gui("ConvertEncoding", new ConvertEncoding());
    	}
    
    }

    3。将拽入文件中数据进行 Base64 编码的子类实现

    Base64Encrypt.java

    package org.bruce.vertices.asist.units;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    import org.bruce.vertices.asist.common.Dta;
    import org.bruce.vertices.asist.common.Gui;
    import org.bruce.vertices.asist.security.Base64;
    import org.bruce.vertices.asist.utils.Functions;
    import org.bruce.vertices.asist.utils.StringFileBridge;
    
    /**
     * @author Bruce Yang
     * 将文件内容以 base64 编码~
     */
    public class Base64Encrypt extends Dta {
    
    	@Override
    	protected void handle(File f) {
    		// TODO Auto-generated method stub
    		FileInputStream fis = null;
    		try {
    			fis = new FileInputStream(f);
    		} catch (FileNotFoundException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		byte[] bytesData = Functions.inputStream2byteArray(fis);
    		
    		String encodedStr = Base64.encode(bytesData);
    		
    		StringFileBridge.string2File(encodedStr, f);
    	}
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		new Gui("Base64Encrypt", new Base64Encrypt());
    	}
    
    }
    如上,这些个可爱的小窗口都包含了一定的功能,而你所要做的,

    仅仅是在重写的 handle() 方法里面书写核心逻辑,

    其他任何不必要的或是不经常要做修改的,都能“眼不见心不烦”,

    这样的主体结构,是否能算的上比较优秀了?

    从此,Gui,要多次被用到的类主体框架结构,拖拽去路径功能,

    都与提供功能的核心逻辑分隔开了,

    再也不用重复的去写类似 

    class MyJFramne extends JFrame ... 

    JFrame jf = new JFrame...

    class MyDropTargetAdapter extends DropTargetAdapter...

    的代码了。

    世界,真美好~

    好了,世界虽然美好了一点儿,但还是无法挡住我追求更美好世界的心。

    如果你仔细观察,肯定也发现了,每一个 Dta 子类实现中都包含了一个程序入口方法:

    public static void main(String[] args) {....

    毫无疑问,对比与 c++ 的 void main(), int main(),

    java的 public static void main(String[] args) { 给我带来过太多的惊喜,

    我曾为每个类下面都能放一个这样的入口方法而感到心旷神怡,

    但是如今,写了n次这般,铁哥们儿也给熬成婆了。

    于是,我便想,如果能只写一次的话,那便太幸福了~

    ok,至此模板策略便要出台了。有了模板,从此便可以这个该死的 public static void main(String[] args)...

    说简单其实也很简单,就是弄一个通用的文件作为模板,如下:

    Dta.txt

    package org.bruce.vertices.asist.units;
    
    import java.io.File;
    
    import org.bruce.vertices.asist.common.Dta;
    import org.bruce.vertices.asist.common.Gui;
    import org.bruce.vertices.asist.utils.StringFileBridge;
    
    /**
     * @author Bruce Yang
     * 
     */
    public class _CLASS_NAME_ extends Dta {
    
    	@Override
    	protected void handle(File f) {
    		// TODO Auto-generated method stub
    		
    		// 将文件内容读取为待处理的字符串~
    		String raw = StringFileBridge.file2String(f, "UTF-8");
    		
    		// Add File Contents Handle-Logic below~
    		
    		
    		// 将处理完毕获得的字符串写回原文件~
    		StringFileBridge.string2File("", f);
    	}
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		new Gui("_CLASS_NAME_", new _CLASS_NAME_());
    	}
    }
    接着,弄一个程序界面,在界面里面加入一个 JTextField 控件,

    本例比较简单,如上面的模板文件 Dta.txt,

    我要做的仅仅是将 _CLASS_NAME_ 替换为实际的类名即可。

    那么,程序界面仅仅需要包含简单的一些逻辑即可:

    1。将模板文件中的数据读成 java String 对象

    2。对 String 对象做字符替换处理(如将  _CLASS_NAME_ 悉数替换为 EncodingConvertor)

    3。将替换完毕后的 String 对象写入系统剪贴板

    4。选中目的包,ctl + v 或 command + v 将剪贴板中的代码变成  类文件

    (我用的是 eclipse,eclipse毫无疑问是具有这般神通的)

    是不是很简单,这样的话,只需要在生成的类文件的 handle() 方法中插入最最核心的代码逻辑即可了。

    如果没有这个小工具的话,至少要经过这么一些步骤(eclipse IDE):

    1。选中目的包新建类

    2。填写 Dta 子类的名称

    3。浏览选中所要继承的父类 Dta,勾选生成主方法单选框。

    点击 finish,至此子类的 java 文件就生成好了,

    5。public static void main() 方法中的 new Gui("类名", new 类名());

    这行是必须要通过手来敲的!!!

    千篇一律的,很有规律,很消磨人对程序编写的热情,仅此而已~

    恩,至此要说的差不多已经说完了,下面把其他两个类文件的代码贴出来:

    TemplateGenerator.java

    package org.bruce.vertices.asist.template;
    
    import java.awt.BorderLayout;
    import java.awt.GridLayout;
    import java.awt.Toolkit;
    import java.awt.datatransfer.Clipboard;
    import java.awt.datatransfer.StringSelection;
    import java.awt.datatransfer.Transferable;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JScrollBar;
    import javax.swing.JScrollPane;
    import javax.swing.JTextArea;
    import javax.swing.JTextField;
    
    /**
     * @author Bruce Yang
     * 主要用于生成模板,只要填入 “单元处理器” 的名称即可将代码模板生成好并置入剪切板。
     * 这样的话,只要新建一个类,统一好所使用的名称,将剪切板里面的代码模板复制到编辑器中就可以了~
     */
    public class TemplateGenerator extends JFrame {
    	private static final long serialVersionUID = 8510818188986391801L;
    	
    	public static final int JFRAME_WIDTH = 500;
    	public static final int JFRAME_HEIGHT = 400;
    	
    	// 用来做中转站将文本转移到系统剪贴板里面~
    	/**
    	 * 2012.05.31.19.32,jtf做中转站还是不行,将换行符给丢失掉了,蛋疼~
    	 */
    //	JTextField jtf_not_visible = null;
    	
    	JTextField jtf = null;
    	JButton jb = null;
    	JTextArea jta = null;
    
    	public TemplateGenerator() {
    		// 1.标题~
    		this.setTitle("TemplateGenerator");
    		
    		// 2。布局管理器~
    		this.setLayout(new BorderLayout());
    		
    		// 3。置入 ui 控件~
    		// ============================================
    //		jtf_not_visible = new JTextField();
    		
    		JPanel jp = new JPanel();
    		jp.setLayout(new GridLayout(1, 5));
    		
    		
    //		JPanel jp_sub = new JPanel();
    //		jp_sub.setLayout(new GridLayout(1, 2));
    //		JLabel jlb = new JLabel(" 模板类名:");
    //		jtf = new JTextField();
    //		jp_sub.add(jlb);
    //		jp_sub.add(jtf);
    //		jp.add(jp_sub);
    		
    		JLabel jlb = new JLabel(spacesGenerator(10) + "模板类名:");
    		jtf = new JTextField();
    		jp.add(jlb);
    		jp.add(jtf);
    		
    		jb = new JButton();
    		jb.setText("生成");
    		jb.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent arg0) {
    				// TODO Auto-generated method stub
    				String className = jtf.getText();
    				
    				String tContents = TemplateLoader.loadTemplateByName("Dta");
    //				System.out.println(tContents);
    				
    				tContents = tContents.replaceAll("_CLASS_NAME_", className);
    				System.out.println(tContents);
    				
    //				String tPreview = tContents.replaceAll("\t", spacesGenerator(8));
    				if(tContents != null && !tContents.equals("")) {
    //					jta.setText(tPreview);
    					
    //					jtf_not_visible.setText(tContents);
    					// 只有被选中的文字才会被弄进系统剪切板,少写这一行,花了我半天时间~
    //					jtf_not_visible.selectAll();
    //					jtf_not_visible.cut();
    //					jtf_not_visible.setText("");
    					
    					/** 直接操作系统剪贴板,摈弃 JTextField 中转站~ */
    					Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
    					Transferable tData = new StringSelection(tContents);
    					clipboard.setContents(tData, null);
    				}
    			}
    		});
    		jp.add(jb);
    		
    		// 占位标签控件(无他用,纯粹占位控制其他控件的排布格式)~
    		JLabel jlb_placeholder0 = new JLabel("");
    		jp.add(jlb_placeholder0);
    		
    //		JLabel jlb_placeholder1 = new JLabel("");
    //		jp.add(jlb_placeholder1);
    		
    		this.add(jp, BorderLayout.NORTH);
    		
    		JScrollPane jsp = new JScrollPane();
    		jta = new JTextArea();
    		jta.setEditable(false);
    		jsp.setViewportView(jta);
    		this.add(jsp, BorderLayout.CENTER);
    		JScrollBar jsb = jsp.getVerticalScrollBar();
    		jsb.setValue(jsb.getMaximum());
    		
    		// ============================================
    		
    		// 4。设置窗体大小和窗口位置~
    		this.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);
    		int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
    		int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
    		this.setLocation((screenWidth-JFRAME_WIDTH)/2, (screenHeight-JFRAME_HEIGHT)/2);
    		
    		/** 关闭的时候将修改过的属性覆盖到 jar 里面的属性配置文件~ */
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		this.setVisible(true);
    	}
    	
    	/**
    	 * 生成由一定数目的空格符连接而成的字符串前缀~
    	 * @param count
    	 * @return
    	 */
    	private String spacesGenerator(int count) {
    		StringBuffer sb = new StringBuffer();
    		for(int i = 0; i < count; ++ i) {
    			sb.append(' ');
    		}
    		return sb.toString();
    	}
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		new TemplateGenerator();
    	}
    
    }
    TemplateLoader.java

    package org.bruce.vertices.asist.template;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.UnsupportedEncodingException;
    
    /**
     * @author Bruce Yang
     * 用于将模板文件中数据加载为供程序使用的字符串对象~
     */
    public class TemplateLoader {
    	
    	public static final String CLASS_PATH = "/org/bruce/vertices/asist/template/";
    	
    	/**
    	 * 按模板文件名称加载~
    	 * @param tName
    	 * @return
    	 */
    	public static String loadTemplateByName(String tName) {
    		String tFileClassPath = CLASS_PATH + tName + ".txt";
    		InputStream is = TemplateLoader.class.getResourceAsStream(tFileClassPath);
    		byte[] bytes = inputStream2byteArray(is);
    		String tContents = null;
    		try {
    			tContents = new String(bytes, "utf-8");
    		} catch (UnsupportedEncodingException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    		return tContents;
    	}
    	
    	/**
    	 * InputStream 转 byte[]~
    	 * @param is
    	 * @return
    	 */
    	public static byte[] inputStream2byteArray(InputStream is) {
    		ByteArrayOutputStream baos = new ByteArrayOutputStream();
    		int i;
    		try {
    			while((i = is.read()) != -1) {
    				baos.write(i);
    			}
    			baos.close();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		byte[] bytes = baos.toByteArray();
    		return bytes;
    	}
    
    	/**
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		// TODO Auto-generated method stub
    		String str = loadTemplateByName("Dta");
    		System.out.println(str);
    	}
    
    }
    收工做饭~



  • 相关阅读:
    每秒处理请求数和并发的关系
    oracle查看表空间及大小
    Oracle 备份还原
    ABOUT BENJAMIN HARDY, PHD
    ServiceActivationException when auto-starting WCF services with AutofacServiceHostFactory
    Welcome Enterprise Skills Initiative
    How to quickly delete all html objects in Excel?
    How does SQLParameter prevent SQL Injection?
    Is it necessary to add a @ in front of an SqlParameter name?
    VS Code Python 全新发布
  • 原文地址:https://www.cnblogs.com/java20130723/p/3212278.html
Copyright © 2020-2023  润新知