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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

用实际源码来举例子吧:

Gui.java

[java]  view plain copy
  1. package org.bruce.vertices.asist.common;  
  2.   
  3. import java.awt.Toolkit;  
  4. import java.awt.dnd.DnDConstants;  
  5. import java.awt.dnd.DropTarget;  
  6.   
  7. import javax.swing.JFrame;  
  8. import javax.swing.JScrollBar;  
  9. import javax.swing.JScrollPane;  
  10. import javax.swing.JTextArea;  
  11.   
  12.   
  13. /** 
  14.  * @author Bruce Yang 
  15.  * 程序 ui~ 
  16.  */  
  17. public class Gui extends JFrame {  
  18.     private static final long serialVersionUID = -7044615012246731094L;  
  19.       
  20.     public static final int JFRAME_WIDTH = 500;  
  21.     public static final int JFRAME_HEIGHT = 400;  
  22.       
  23.     /** 
  24.      * @param title “单元处理器”的名称 
  25.      * @param dtl   DropTargetListener 实例~ 
  26.      */  
  27.     public Gui(String title, Dta dtl) {  
  28.         this.setTitle(title);  
  29.           
  30.         this.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);  
  31.         JScrollPane jsp = new JScrollPane();  
  32.         JTextArea jta = new JTextArea();  
  33.         jsp.setViewportView(jta);  
  34.         this.add(jsp);  
  35.         JScrollBar jsb = jsp.getVerticalScrollBar();  
  36.         jsb.setValue(jsb.getMaximum());   
  37.           
  38.         int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;  
  39.         int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;  
  40.           
  41.         this.setLocation((screenWidth-JFRAME_WIDTH)/2, (screenHeight-JFRAME_HEIGHT)/2);  
  42.           
  43.         /** 关闭的时候将修改过的属性覆盖到 jar 里面的属性配置文件~ */  
  44.         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  45.         new DropTarget(jta, DnDConstants.ACTION_COPY_OR_MOVE, dtl);  
  46.         this.setVisible(true);  
  47.     }  
  48. }  
Gui.java 是 “Drag && Drop” 类工具程序的界面窗口

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

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

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

Dta.java

[java]  view plain copy
  1. package org.bruce.vertices.asist.common;  
  2.   
  3. import java.awt.datatransfer.DataFlavor;  
  4. import java.awt.dnd.DnDConstants;  
  5. import java.awt.dnd.DropTargetAdapter;  
  6. import java.awt.dnd.DropTargetDropEvent;  
  7. import java.io.File;  
  8. import java.util.ArrayList;  
  9. import java.util.Iterator;  
  10. import java.util.List;  
  11.   
  12. import org.bruce.vertices.asist.utils.Functions;  
  13.   
  14. /** 
  15.  * @author Bruce Yang 
  16.  * 拖拽监听~ 
  17.  */  
  18. public abstract class Dta extends DropTargetAdapter {  
  19.       
  20.     /** 
  21.      * @param f 
  22.      */  
  23.     protected abstract void handle(File f);  
  24.       
  25.     /* (non-Javadoc) 
  26.      * @see java.awt.dnd.DropTargetListener#drop(java.awt.dnd.DropTargetDropEvent) 
  27.      */  
  28.     @SuppressWarnings("unchecked")  
  29.     public void drop(DropTargetDropEvent event) {  
  30.         if (event.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {  
  31.             event.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);  
  32.               
  33.             DataFlavor df = DataFlavor.javaFileListFlavor;  
  34.             List<File> list = null;  
  35.             try {  
  36.                 list = (List<File>)(event.getTransferable().getTransferData(df));  
  37.             } catch (Exception e) {  
  38.                 e.printStackTrace();  
  39.             }  
  40.               
  41.             Iterator<File> iterator = list.iterator();  
  42.             while (iterator.hasNext()) {  
  43.                 File item = iterator.next();  
  44.                   
  45.                 List<File> fileList = new ArrayList<File>();  
  46.                 if(item.isDirectory()) {  
  47.                     Functions.listFilesOnly(item, fileList);  
  48.                 } else {  
  49.                     fileList.add(item);  
  50.                 }  
  51.                   
  52.                 for(File f : fileList) {  
  53.                     handle(f);  
  54.                 }  
  55.             }  
  56.             event.dropComplete(true);  
  57.         } else {  
  58.             event.rejectDrop();  
  59.         }  
  60.     }  
  61. }  
Dta.java 是 DropTargetAdapter 类的子类,用来为 Gui 类的 JTextArea 控件增加拖拽取文件路径的功能。

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

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

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

下面给出几个例子:

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

PrintAbsPath.java

[java]  view plain copy
  1. package org.bruce.vertices.asist.units;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.bruce.vertices.asist.common.Dta;  
  6. import org.bruce.vertices.asist.common.Gui;  
  7.   
  8. /** 
  9.  * @author Bruce Yang 
  10.  * 这么一来就清晰多了~ 
  11.  */  
  12. public class PrintAbsPath extends Dta {  
  13.   
  14.     @Override  
  15.     public void handle(File f) {  
  16.         // TODO Auto-generated method stub  
  17.         System.out.println(f.getAbsolutePath());  
  18.     }  
  19.   
  20.     /** 
  21.      * @param args 
  22.      */  
  23.     public static void main(String[] args) {  
  24.         // TODO Auto-generated method stub  
  25.         new Gui("PrintAbsPath"new PrintAbsPath());  
  26.     }  
  27. }  

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

ConvertEncoding.java

[java]  view plain copy
  1. package org.bruce.vertices.asist.units;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.bruce.vertices.asist.common.Dta;  
  6. import org.bruce.vertices.asist.common.Gui;  
  7. import org.bruce.vertices.asist.utils.StringFileBridge;  
  8.   
  9. /** 
  10.  * @author Bruce Yang 
  11.  * 改变文件的字符编码(GBK -> UTF-8)~ 
  12.  */  
  13. public class ConvertEncoding extends Dta {  
  14.     private static final String FROM = "GBK";  
  15.     private static final String TO = "UTF-8";  
  16.   
  17.     @Override  
  18.     protected void handle(File f) {  
  19.         // TODO Auto-generated method stub  
  20.         String rawStr = StringFileBridge.file2String(f, FROM);  
  21.         String convertedStr = StringFileBridge.changeEncode(rawStr, TO);  
  22.           
  23.         StringFileBridge.string2File(convertedStr, f);  
  24.     }  
  25.       
  26.     /** 
  27.      * @param args 
  28.      */  
  29.     public static void main(String[] args) {  
  30.         new Gui("ConvertEncoding"new ConvertEncoding());  
  31.     }  
  32.   
  33. }  

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

Base64Encrypt.java

[java]  view plain copy
  1. package org.bruce.vertices.asist.units;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileInputStream;  
  5. import java.io.FileNotFoundException;  
  6.   
  7. import org.bruce.vertices.asist.common.Dta;  
  8. import org.bruce.vertices.asist.common.Gui;  
  9. import org.bruce.vertices.asist.security.Base64;  
  10. import org.bruce.vertices.asist.utils.Functions;  
  11. import org.bruce.vertices.asist.utils.StringFileBridge;  
  12.   
  13. /** 
  14.  * @author Bruce Yang 
  15.  * 将文件内容以 base64 编码~ 
  16.  */  
  17. public class Base64Encrypt extends Dta {  
  18.   
  19.     @Override  
  20.     protected void handle(File f) {  
  21.         // TODO Auto-generated method stub  
  22.         FileInputStream fis = null;  
  23.         try {  
  24.             fis = new FileInputStream(f);  
  25.         } catch (FileNotFoundException e) {  
  26.             // TODO Auto-generated catch block  
  27.             e.printStackTrace();  
  28.         }  
  29.         byte[] bytesData = Functions.inputStream2byteArray(fis);  
  30.           
  31.         String encodedStr = Base64.encode(bytesData);  
  32.           
  33.         StringFileBridge.string2File(encodedStr, f);  
  34.     }  
  35.   
  36.     /** 
  37.      * @param args 
  38.      */  
  39.     public static void main(String[] args) {  
  40.         // TODO Auto-generated method stub  
  41.         new Gui("Base64Encrypt"new Base64Encrypt());  
  42.     }  
  43.   
  44. }  
如上,这些个可爱的小窗口都包含了一定的功能,而你所要做的,

仅仅是在重写的 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

[plain]  view plain copy
  1. package org.bruce.vertices.asist.units;  
  2.   
  3. import java.io.File;  
  4.   
  5. import org.bruce.vertices.asist.common.Dta;  
  6. import org.bruce.vertices.asist.common.Gui;  
  7. import org.bruce.vertices.asist.utils.StringFileBridge;  
  8.   
  9. /**  
  10.  * @author Bruce Yang  
  11.  *   
  12.  */  
  13. public class _CLASS_NAME_ extends Dta {  
  14.   
  15.     @Override  
  16.     protected void handle(File f) {  
  17.         // TODO Auto-generated method stub  
  18.           
  19.         // 将文件内容读取为待处理的字符串~  
  20.         String raw = StringFileBridge.file2String(f, "UTF-8");  
  21.           
  22.         // Add File Contents Handle-Logic below~  
  23.           
  24.           
  25.         // 将处理完毕获得的字符串写回原文件~  
  26.         StringFileBridge.string2File("", f);  
  27.     }  
  28.   
  29.     /**  
  30.      * @param args  
  31.      */  
  32.     public static void main(String[] args) {  
  33.         // TODO Auto-generated method stub  
  34.         new Gui("_CLASS_NAME_", new _CLASS_NAME_());  
  35.     }  
  36. }  
接着,弄一个程序界面,在界面里面加入一个 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

[java]  view plain copy
  1. package org.bruce.vertices.asist.template;  
  2.   
  3. import java.awt.BorderLayout;  
  4. import java.awt.GridLayout;  
  5. import java.awt.Toolkit;  
  6. import java.awt.datatransfer.Clipboard;  
  7. import java.awt.datatransfer.StringSelection;  
  8. import java.awt.datatransfer.Transferable;  
  9. import java.awt.event.ActionEvent;  
  10. import java.awt.event.ActionListener;  
  11.   
  12. import javax.swing.JButton;  
  13. import javax.swing.JFrame;  
  14. import javax.swing.JLabel;  
  15. import javax.swing.JPanel;  
  16. import javax.swing.JScrollBar;  
  17. import javax.swing.JScrollPane;  
  18. import javax.swing.JTextArea;  
  19. import javax.swing.JTextField;  
  20.   
  21. /** 
  22.  * @author Bruce Yang 
  23.  * 主要用于生成模板,只要填入 “单元处理器” 的名称即可将代码模板生成好并置入剪切板。 
  24.  * 这样的话,只要新建一个类,统一好所使用的名称,将剪切板里面的代码模板复制到编辑器中就可以了~ 
  25.  */  
  26. public class TemplateGenerator extends JFrame {  
  27.     private static final long serialVersionUID = 8510818188986391801L;  
  28.       
  29.     public static final int JFRAME_WIDTH = 500;  
  30.     public static final int JFRAME_HEIGHT = 400;  
  31.       
  32.     // 用来做中转站将文本转移到系统剪贴板里面~  
  33.     /** 
  34.      * 2012.05.31.19.32,jtf做中转站还是不行,将换行符给丢失掉了,蛋疼~ 
  35.      */  
  36. //  JTextField jtf_not_visible = null;  
  37.       
  38.     JTextField jtf = null;  
  39.     JButton jb = null;  
  40.     JTextArea jta = null;  
  41.   
  42.     public TemplateGenerator() {  
  43.         // 1.标题~  
  44.         this.setTitle("TemplateGenerator");  
  45.           
  46.         // 2。布局管理器~  
  47.         this.setLayout(new BorderLayout());  
  48.           
  49.         // 3。置入 ui 控件~  
  50.         // ============================================  
  51. //      jtf_not_visible = new JTextField();  
  52.           
  53.         JPanel jp = new JPanel();  
  54.         jp.setLayout(new GridLayout(15));  
  55.           
  56.           
  57. //      JPanel jp_sub = new JPanel();  
  58. //      jp_sub.setLayout(new GridLayout(1, 2));  
  59. //      JLabel jlb = new JLabel(" 模板类名:");  
  60. //      jtf = new JTextField();  
  61. //      jp_sub.add(jlb);  
  62. //      jp_sub.add(jtf);  
  63. //      jp.add(jp_sub);  
  64.           
  65.         JLabel jlb = new JLabel(spacesGenerator(10) + "模板类名:");  
  66.         jtf = new JTextField();  
  67.         jp.add(jlb);  
  68.         jp.add(jtf);  
  69.           
  70.         jb = new JButton();  
  71.         jb.setText("生成");  
  72.         jb.addActionListener(new ActionListener() {  
  73.             @Override  
  74.             public void actionPerformed(ActionEvent arg0) {  
  75.                 // TODO Auto-generated method stub  
  76.                 String className = jtf.getText();  
  77.                   
  78.                 String tContents = TemplateLoader.loadTemplateByName("Dta");  
  79. //              System.out.println(tContents);  
  80.                   
  81.                 tContents = tContents.replaceAll("_CLASS_NAME_", className);  
  82.                 System.out.println(tContents);  
  83.                   
  84. //              String tPreview = tContents.replaceAll("\t", spacesGenerator(8));  
  85.                 if(tContents != null && !tContents.equals("")) {  
  86. //                  jta.setText(tPreview);  
  87.                       
  88. //                  jtf_not_visible.setText(tContents);  
  89.                     // 只有被选中的文字才会被弄进系统剪切板,少写这一行,花了我半天时间~  
  90. //                  jtf_not_visible.selectAll();  
  91. //                  jtf_not_visible.cut();  
  92. //                  jtf_not_visible.setText("");  
  93.                       
  94.                     /** 直接操作系统剪贴板,摈弃 JTextField 中转站~ */  
  95.                     Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();  
  96.                     Transferable tData = new StringSelection(tContents);  
  97.                     clipboard.setContents(tData, null);  
  98.                 }  
  99.             }  
  100.         });  
  101.         jp.add(jb);  
  102.           
  103.         // 占位标签控件(无他用,纯粹占位控制其他控件的排布格式)~  
  104.         JLabel jlb_placeholder0 = new JLabel("");  
  105.         jp.add(jlb_placeholder0);  
  106.           
  107. //      JLabel jlb_placeholder1 = new JLabel("");  
  108. //      jp.add(jlb_placeholder1);  
  109.           
  110.         this.add(jp, BorderLayout.NORTH);  
  111.           
  112.         JScrollPane jsp = new JScrollPane();  
  113.         jta = new JTextArea();  
  114.         jta.setEditable(false);  
  115.         jsp.setViewportView(jta);  
  116.         this.add(jsp, BorderLayout.CENTER);  
  117.         JScrollBar jsb = jsp.getVerticalScrollBar();  
  118.         jsb.setValue(jsb.getMaximum());  
  119.           
  120.         // ============================================  
  121.           
  122.         // 4。设置窗体大小和窗口位置~  
  123.         this.setSize(JFRAME_WIDTH, JFRAME_HEIGHT);  
  124.         int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;  
  125.         int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;  
  126.         this.setLocation((screenWidth-JFRAME_WIDTH)/2, (screenHeight-JFRAME_HEIGHT)/2);  
  127.           
  128.         /** 关闭的时候将修改过的属性覆盖到 jar 里面的属性配置文件~ */  
  129.         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  
  130.         this.setVisible(true);  
  131.     }  
  132.       
  133.     /** 
  134.      * 生成由一定数目的空格符连接而成的字符串前缀~ 
  135.      * @param count 
  136.      * @return 
  137.      */  
  138.     private String spacesGenerator(int count) {  
  139.         StringBuffer sb = new StringBuffer();  
  140.         for(int i = 0; i < count; ++ i) {  
  141.             sb.append(' ');  
  142.         }  
  143.         return sb.toString();  
  144.     }  
  145.   
  146.     /** 
  147.      * @param args 
  148.      */  
  149.     public static void main(String[] args) {  
  150.         // TODO Auto-generated method stub  
  151.         new TemplateGenerator();  
  152.     }  
  153.   
  154. }  
TemplateLoader.java

[java]  view plain copy
  1. package org.bruce.vertices.asist.template;  
  2.   
  3. import java.io.ByteArrayOutputStream;  
  4. import java.io.IOException;  
  5. import java.io.InputStream;  
  6. import java.io.UnsupportedEncodingException;  
  7.   
  8. /** 
  9.  * @author Bruce Yang 
  10.  * 用于将模板文件中数据加载为供程序使用的字符串对象~ 
  11.  */  
  12. public class TemplateLoader {  
  13.       
  14.     public static final String CLASS_PATH = "/org/bruce/vertices/asist/template/";  
  15.       
  16.     /** 
  17.      * 按模板文件名称加载~ 
  18.      * @param tName 
  19.      * @return 
  20.      */  
  21.     public static String loadTemplateByName(String tName) {  
  22.         String tFileClassPath = CLASS_PATH + tName + ".txt";  
  23.         InputStream is = TemplateLoader.class.getResourceAsStream(tFileClassPath);  
  24.         byte[] bytes = inputStream2byteArray(is);  
  25.         String tContents = null;  
  26.         try {  
  27.             tContents = new String(bytes, "utf-8");  
  28.         } catch (UnsupportedEncodingException e) {  
  29.             // TODO Auto-generated catch block  
  30.             e.printStackTrace();  
  31.         }  
  32.         return tContents;  
  33.     }  
  34.       
  35.     /** 
  36.      * InputStream 转 byte[]~ 
  37.      * @param is 
  38.      * @return 
  39.      */  
  40.     public static byte[] inputStream2byteArray(InputStream is) {  
  41.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  42.         int i;  
  43.         try {  
  44.             while((i = is.read()) != -1) {  
  45.                 baos.write(i);  
  46.             }  
  47.             baos.close();  
  48.         } catch (IOException e) {  
  49.             e.printStackTrace();  
  50.         }  
  51.         byte[] bytes = baos.toByteArray();  
  52.         return bytes;  
  53.     }  
  54.   
  55.     /** 
  56.      * @param args 
  57.      */  
  58.     public static void main(String[] args) {  
  59.         // TODO Auto-generated method stub  
  60.         String str = loadTemplateByName("Dta");  
  61.         System.out.println(str);  
  62.     }  
  63.   
  64. }  
收工做饭~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值