Alex http://zhk.me I always thought love was an abstract class until you made an instance of it. Sat, 16 May 2020 08:18:16 +0000 zh-CN hourly 1 https://wordpress.org/?v=5.4.1 http://zhk.me/wp-content/uploads/2019/09/cropped-site-icon-32x32.png Alex http://zhk.me 32 32 Git clean 命令 http://zhk.me/1631.html Sat, 16 May 2020 08:18:16 +0000 http://zhk.me/?p=1631 Read More »]]> 删除本地untracked,ignored的文件或文件夹。

Untracked

删文件:git clean –f

删文件夹:git clean –fd

Ignored

Only ignored: git clean –fX

Ignored and non-ignored: git clean –fx

预演删除

-n: 预演,输出要删除什么。

可以随意加变量组合,例如:git clean –fxdn

]]>
Maven build RCP projects — 引入不存在的artifacts http://zhk.me/1627.html Wed, 18 Mar 2020 08:51:03 +0000 http://zhk.me/?p=1627 Read More »]]> 最近Build项目时,发现build出的程序打不开。看build log,发现一个已经被删掉的bundle被引入进来,导致build出的程序不能用。

log记录:[WARNING] The following locally built units have been used to resolve project dependencies. XXX/1.0.0

问题原因

和普通maven build一样,Tycho build会使用本地Maven已经install了的artifacts。RCP项目有个target platform文件,Maven本地installed的artifacts会被加入target platform。当你只需要build部分artifacts时,这个特性就很有用。

解决办法

公司项目是在build机器上自动build的,而且build机器又不止一个,我不能去把每个机器上本地install了的XXX/1.0.0 artifacts删去。不过可以在maven命令后面加上-Dtycho.localArtifacts=ignore,这样build项目是就不会用本地artifacts。

]]>
屏蔽百度网页里的广告 http://zhk.me/1566.html Sun, 13 Oct 2019 08:12:14 +0000 http://zhk.me/?p=1566 Read More »]]> 别的不说,光是百度搜索右边的广告,都看着让人烦。本是要找点资料,它非要给你展示些无用东西,分散注意力,引诱用户点击,浪费用户的时间。

使用Chrome可以安装一个插件,来禁止百度网页上的广告。打开网址:http://chromecj.com/,搜索“屏蔽百度推广”。

现在Chrome不允许拖拽安装自己下载的插件,安装步骤如下:

1. 把下载的文件后缀改为.zip (20191013153703chromecj.com.crx —> 20191013153703chromecj.com.zip)

2. 解压zip文件。注意:解压后的folder不能删除,Chrome每次启动都会从该folder里加载插件。

3. 打开Chrome 扩展程序页面 (chrome://extensions),开启“开发者模式”,点击“加载已解压的扩展程序”,然后选择刚才解压的folder。

]]>
Flicker ID和API Key http://zhk.me/1538.html Mon, 30 Sep 2019 03:32:20 +0000 http://zhk.me/?p=1538 Read More »]]> 使用 Slickr Flickr需要填写Flickr ID 和 Flickr API Key.

1. 获取Flickr ID

它提示的链接不管用了。需要登录自己的Flickr账户,URL后缀就是ID。

image

2. 获取Flickr API Key.

按照它的提示,点击”Get your Flickr API keys。

]]>
Nebula Grid总是捕获鼠标滚轮事件 http://zhk.me/1483.html Fri, 27 Sep 2019 03:23:02 +0000 http://zhk.me/?p=1483 Read More »]]> Nebula Grid是一个第三方SWT控件,用于以Table和Tree形式显示数据,但是功能比JFace Table和Tree更强大些。

JFace Table 或 Tree在其不显示滚动条时,鼠标滚轮轮动外面控件的滚动条。

image

当JFace Table 或 Tree 显示滚动条时,鼠标滚轮滚动Table 或 Tree自己。

image

但是Nebula Grid在不显示滚动条时,不能滚动外面控件的滚动条,这个有点不太user friendly。解决这个问题的方法是:在滚动条不显示时,把它设置成disabled,这样就不会捕获鼠标滚轮事件,可以滚动外面控件的滚轮了。

public static void disableVerticalBarWhenInvisible(GridTableViewer gridTableViewer) {
    gridTableViewer.getGrid().addPaintListener(event -> {
      ScrollBar verticalBar = ((Grid) event.widget).getVerticalBar();
      if (verticalBar != null && verticalBar.isEnabled() ^ verticalBar.isVisible()) {
        verticalBar.setEnabled(verticalBar.isVisible());
      }
    });
  }

封装成了一个方法,方便使用。在每次paint时,检查滚动条enabled属性是否和visible属性一直,如果不一致,就修改enabled属性。

]]>
import wildcard in Java http://zhk.me/1457.html http://zhk.me/1457.html#comments Sun, 04 Aug 2019 13:57:45 +0000 http://zhk.me/?p=1457 Read More »]]> 
import pacakage.*;


很多人都知道要避免import wildcard,那为什么import all不好呢?也许有人认为,import wildcard会导致import太多不必要的类,会导致类很臃肿或者运行变慢。

其实:


import java.util.Date;



import java.util.*;


它们在性能上没有有任何区别,import语句只是告诉compiler在哪里寻找依赖的类。编译成的class文件中是没有import语句的,例如下面的class文件:

屏幕快照 2019 08 04 下午9 20 49

或者可以说import是个语法糖,如果没有import语句,就要在每个使用到的外部类(同包里的除外)前面加上package路径。


javax.swing.JButton but = new  javax.swing.JButton();


为什么不用import wildcard

防止类冲突。项目中可能有重名的类,不同工具包也有重名类,例如下面两个包都包含Date,此时编译器会报错。

屏幕快照 2019 08 04 下午9 45 33

Google Java代码规范中,要明确import使用的类,避免import wildcard。

但是我在Clean Code书的17.5章节看到,作者说建议使用import wildcard。理由是:过长的导入清单让读者望而却步,不想让80行的导入语句搞乱代码顶部,import wildcard会让类简洁。这个观点我不太认同,IDE会自动把impor语句t收缩成一行,而且在Eclipse中,ctrl + shift + O可以自动导入需要的类。

]]>
http://zhk.me/1457.html/feed 1
Clean Code – System http://zhk.me/1452.html Sun, 28 Jul 2019 10:01:56 +0000 http://zhk.me/?p=1452 Read More »]]> “复杂要人命,它消磨开发者的生命,让产品难以规划、构建和测试” — Ray Ozzi, 微软首席技术官

1. 如何建造一座城市

你能掌握一切细节吗?大概不行。即便是管理一个既存的城市,也是一个人无法做到的。城市能运转,是因为它演化出恰当的抽象等级和模块,好让个人和他们工作的部门即便在不了解全局时也能有效的运转。

软件设计也是这样,要将关注面分离以及对系统抽象分层。

2. 类不要自己构造依赖对象


public class Worker {
  public void doSomething() {
    Service service = new MyServiceImpl(new Component1(), new Component2());
    /* Do something */
  }
}

上面的代码不好,对象的使用和构造应当分离,Work类不应当知道MyServiceImpl怎么创建。主要有两个严重问题。

第一,不便于测试。很难mock MyServiceImpl类,虽然PowerMokito可以做到。

第二,违反了单一职责原则。Work多了一个职责去管理创建Service,还增加了三个不必要的依赖:MyServiceImpl, Component1和Component2。当MyServiceImple的构造方法变化时,Work类也要变化。

2.1 从外面传入Service

如果Service对象只是本方法使用,可以从该方法传入。


public class Worker {
  public void doSomething(Service service) {
    /* Do something */
  }
}

如果Worker类中很多方法都使用Service,可以将Service声明为field,使用set方法传入(当然也可以在构造方法中传入)。


public class Worker {
  private Service service;
    public void doSomething() {
    /* Do something */
  }

  public void setService(Service service) {
    this.service = service;
  }
}

2.2 使用工厂对象

Worker调用ServiceFactory(抽象工厂)生成Service,来解耦和MyServiceImpl的依赖。


public class Worker {
  private ServiceFactory serviceFactory;

  public void doSomething() {
    Service service = serviceFactory.createService();
    /* Do something */
  }
}

2.3 使用依赖注入

有一种强大的机制可以实现分离构造和使用,那就是控制反转(Inversion of Control, IoC)和依赖注入(Dependency Injection, DI)。控制反转和依赖注入是同一个东西,只是看问题角度不一样。一开始被称为控制反转,指对象不自己实例化依赖对象,将这部分职责反转给其它对象或容器。由于控制反转这一概念不够形象,2004年Martin Fowler给它起了个新的名字“依赖注入”。依赖注入,明确表明类使用的依赖是注入进来的,而不类自己创建的。

Spring框架tight了最有名的Java DI容器,从一开始的配置XML,到后来的Annotation,再到后面的Auto Scan(自动扫描),使用起来越来越方便。用户可以设置容器中对象是单例的还是每次生成新的对象,以及是否是Lazy(调用时才创建对象)等。

3. 扩容

城市由城镇而来,城镇由聚居而来。一开始道路狭窄,几乎无人涉足,随后逐渐拓宽。有多少次,你开着车艰难穿过一个“道路整改”工程,心想“它们为什么不一开始就修条够宽的路呢?!”。那样是不现实的,谁敢打包票说在一个小镇修建一条六车道的公路并不浪费呢?谁会想要那么一条公路穿过他们小镇呢?

“一开始就做对系统”纯属神话。反之,我们应该只去实现今天的用户需求,然后重构,明天再扩展系统,实现新的用户需求,这就是迭代和增量敏捷的精髓。要实现这样的系统,我们要将持续关注的切面恰当分离,说白了就是使用切面编程,每个Aspect各自完成自己的功能。

未分离切面设计

EJB1和EJB2架构没有恰当分离切面,本地EJB接口需要继承java.ejb.EJBLocalObject,Entyty Bean需要实现javax.ejb.EntityBean接口。业务逻辑与EJB2容器紧密耦合,面向对象本身也被侵蚀,因为继承了EJB的类就不能再继承其它的类了。

分类切面设计

EJB3通过XML或Annotation分离切面。下面的Bank是Entity Bean,它不需要了解容器,通过Annotation(也可以用XML)EJB3就知道如何将其和DB数据映射。


@Entity
@Table(name = "BANKS")
public class Banker implements java.io.Serializable {
  @ID @GeneratedValue(strategy=GenerationType.AUTO)
  private int id;
    /*Other fields*/
}

下面来看看Java中的三种切面编程方式。

3.1 Java动态代理

JDK的动态代理对象必须实现某个接口,如果代理对象没有实现接口,则需要使用CGLIB等字节码工具。下面是Java动态代理的例子:


interface Plane {
  void fly();
}

interface Animal {
  void run();
}

public static void main(String[] args) {
  Object proxyObj = Proxy.newProxyInstance(
      Tester.class.getClassLoader(),
      new Class[] {Animal.class, Plane.class},
      new InvocationHandler() {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if ("run".equals(method.getName())) {
        System.out.println("Animal runs in the field.");
      } else if ("fly".equals(method.getName())) {
        System.out.println("Plane flys in the sky.");
      }
      return null;
    }
  });

  ((Animal) proxyObj).run();
  ((Plane) proxyObj).fly();
}

输出结果:


Animal runs in the field.
Plane flys in the sky.

3.2 Spring AOP编程

Spring AOP支持使用XML或Annotation声明切面,下面是使用@Around的Example:

NewImage


@Aspect
public class Audiences {

  @Pointcut("execution(* com.test.aop.Performance.perform(..))")
  public void perform() {}

  @Around("perform()")
  public void watchPerformance(ProceedingJoinPoint jp) {
    try {
      Object[] args = jp.getArgs();

      System.out.println("Silencing cell phone");
      System.out.println("Taking seats");

      jp.proceed();

      System.out.println("CLAP CLAP CLAP!!!" + " Cool " + args[0]);
    } catch (Throwable e) {
      System.out.println("Demanding a refund.");
    }
  }

}

当执行Performance类的perform方法时,它就会被代理。

3.3 AspectJ 切面编程

在切面编程中,80% – 90%情况下,Spring AOP 足以满足需求。当我们需要代理构造方法时,既类构造前后执行某些操作,Spring AOP就不胜任,需要使用AspectJ。AspectJ有自己的AOP语法,使用专门的编译器生成代理Class,也就是说编译期代理。Spring AOP主要使用CGLIB在运行期进行代理。

4. 小结

使用AOP编程,我们就可以使用POJO编写应用程序的领域逻辑,在代码层面和架构关注面分离,这样才能真正地用测试来驱动架构

没有必要先做大设计(Big Desgn Up Front, BDUF),DBUF是有害的,它阻碍改进,因为心理上会抵制丢弃既成之事,也因为架构上的方案选择影响到后续的设计思路。应当从简单但切分良好的架构开始软件项目,快速交付可以工作的产品,随着规模的增加来添加更多基础架构。

系统也应该是简洁的。侵害性架构会湮灭领域逻辑,冲击敏捷能力。当领域逻辑受到困扰,质量也就堪忧。无论是设计系统或者单独的模块,别忘了使用大概可以工作的最简单方案

]]>
RxJava doOnComplete(…)运行在哪个线程上 http://zhk.me/1449.html Thu, 11 Jul 2019 03:07:38 +0000 http://zhk.me/?p=1449 Read More »]]> observerOn(…)会创建一个新的Stream,doOnComplete(…)相对于observeOn(…)的前后关系,决定doOnComplete(…)运行在哪个线程上。

  • 之前:运行在前面Stream所使用的线程。
  • 之后:运行在新建Stream所使用的线程。

Example:

Observable.just(1)
    .subscribeOn(Schedulers.io())
    .doOnNext(val -> System.out.println("doOnNext: " + Thread.currentThread()))
    .doOnComplete(() -> System.out.println("doOnComplete1: " + Thread.currentThread()))
    .observeOn(Schedulers.computation())
    .doOnComplete(() -> System.out.println("doOnComplete2: " + Thread.currentThread()))
    .subscribe(val -> System.out.println("subscribe: " + Thread.currentThread()));

输出结果:

image

]]>
使用Maven对Eclipse RCP程序测试 http://zhk.me/1437.html Tue, 09 Jul 2019 13:29:55 +0000 http://zhk.me/?p=1437 Read More »]]> 在项目的RCP程序中有两类代码需要测试:

  • 一类是testSourceDirectory里的测试代码,由maven-surefire-plugin执行。
  • 一类是单独的test plugin里的测试代码,由tycho-surefire-plugin执行。

1. 测试testSourceDirectory

testSourceDirectory默认是src/test/java,可以在pom中修改,Eclipse plugin project的源代码在src folder里,对应的测试代码通常就放在test folder里。pom文件设置:


<build>
  <sourceDirectory>src</sourceDirectory>
  <testSourceDirectory>test</testSourceDirectory>
</build>


对于packaging类型为eclipse-plugin,eclipse-test-plugin等tycho类型的Mave项目,需要明白以下两点:

  1. Maven使用tycho-compiler-plugin编译plugin,tycho-compiler-plugin没有testCompile goal,所以testSourceDirectory里的代码不会被编译。
  2. maven-compiler-plugin有testCompile goal,但是effective pom里没有,默认不会执行。

所以为了能够测试testSourceDirectory,我们需要添加maven-compiler-plugin来编译测试代码,然后使用maven-surefire-plugin运行unit test。务必写明在哪个phase执行哪个goal,effective pom里默认没有,所以不会继承任何<execution/>设置。


<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>2.5.1</version>
  <executions>
    <execution>
      <id>compiletests</id>
      <phase>test-compile</phase>
      <goals>
        <goal>testCompile</goal>
      </goals>
    </execution>
  </executions>
</plugin>

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-surefire-plugin</artifactId>
  <version>2.22.2</version>
  <executions>
    <execution>
      <id>test</id>
      <phase>test</phase>
      <goals>
        <goal>test</goal>
      </goals>
    </execution>
  </executions>
</plugin>


2. 测试单独的test plugin

test plugin是运行在OSGI环境中的,会先加载其所依赖的plugin,然后执行本身的unit test,如果有UI的话,会打开相应的UI。

2.1 设置Target Platform

添加一个target platform plugin,在Eclipse中作为我们的开发、运行环境,在Maven中作为我们的运行环境。

Eclipse Plugin-in Development Environment这个非常重要,plugin test依赖的junit和junit runtime包含在这个feature里面。

屏幕快照 2019 07 09 下午10 04 44

2.2 配置POM

在pom中聚合这个target platform plugin,并且使用target-platorm-configuration插件配置maven在构建过程中使用该target platform。


<modules>
  <module>../tychodemo.target</module>
</modules>

<build>
  <plugins>
      <plugin>
        <groupId>org.eclipse.tycho</groupId>
        <artifactId>target-platform-configuration</artifactId>
        <version>${tycho-version}</version>
        <configuration>
          <target>
            <artifact>
              <groupId>tychodemo</groupId>
              <artifactId>tychodemo.target</artifactId>
              <version>0.0.1-SNAPSHOT</version>
            </artifact>
          </target>
          <environments>
            <!-- Specify environment value according to system type -->
            <environment>
              <os>macosx</os>
              <ws>cocoa</ws>
              <arch>x86_64</arch>
            </environment>
            <!--
            <environment>
              <os>win32</os>
              <ws>win32</ws>
              <arch>x86_64</arch>
            </environment>
            <environment>
              <os>linux</os>
              <ws>gtk</ws>
              <arch>x86_64</arch>
            </environment>
            -->
          </environments>
        </configuration>
      </plugin>
    </plugins>
</build>


tycho-surefire-plugin的配置:

  • <useUIHarness/>为false,表示使用headless mode运行,即使没有Display也可以运行;
  • <useUIThread/>为true,表示使用UI线程。
  • <providerHint/>为junit4,表示使用junit4执行unit test。Indigo只支持junit3,即使写成junit4,junit4的unit test也不会被执行。

<plugin>
  <groupId>org.eclipse.tycho</groupId>
  <artifactId>tycho-surefire-plugin</artifactId>
  <version>${tycho-version}</version>
  <configuration>
    <useUIHarness>false</useUIHarness>
    <useUIThread>true</useUIThread>
    <providerHint>junit4</providerHint>
  </configuration>
</plugin>


2.3 给test plugin添加依赖

test plugin运行在OSGI环境中,dependencies中添加:

  • org.junit:只能是RCP environment自己的junit。手动在class path中添加一个junit,或者使用自己封装的third-party plugin里的junit,都不work。运行时说,cannot specify target Test Framework, available is junit3, junit4, junit47, junit5。这句英文是我凭记忆写的,原message应该不是这样的。这个错误,就算在tycho-surefire-plugin里指定Test Framework也不work。
  • org.eclipse.jdt.junit.runtime:这个plugin也是RCP environment里的,用于发现和执行测试类。如果没有这个依赖,一个测试也不会执行,最后说no test is found。

屏幕快照 2019 07 09 下午10 07 06

2.4 编写测试代码

Test plugin中的测试代码,要放在src folder里,不能放在test folder里,因为tycho-surefire-plugin只会在src folder里寻找test classes。当然我们也可以在test folder里写测试测试类,但这些测试类只能被maven-surefire-plugin执行(前提是配置了maven-surefire-plugin)。

Plug-in test中,可以访问UI。也就是可以测试UI,例如测试当前OSGI环境中某个Service是否注册了;打开一个UI,检测各个Widget是否正确等。

注意:在Eclipse Indigo 3.7.2环境中,plugin unit test要使用Junit 3的写法,继承TestCase类。Junit 4的annotation写法,经过测试,Eclipse上执行class loader抛异常;maven命令行执行,无法识别这些测试类。

Example:


public class HelloPlutinTest extends TestCase {

  public void testShellExist() {
    assertNotNull(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell());
  }

}


2.5 运行plugin test

Eclipse上执行:右键 —> Run As / Debug As —> Junit Plug-in Test。

Maven命令行执行:tycho-surefire-plugin绑定在integration-test phase上,执行mvn integration-test便可。

3. 总结

如果项目中的Junit Plug-in Test不测UI,它就跟普通的Junit测试没啥两样,但是性能不如普通的Junit测试,因为Junit Plugin-in Test要初始化运行环境,加载需要的plugin等。

unit test的执行要尽可能的快,所以如果Junit Plug-in Test里面没有测试UI的类,我倾向于使用maven-surefire-plugin来执行。

小技巧:将Junit Plugin-in Test工程的source folder从src改为test,它们就会变被maven-surefire-plugin执行,而tycho-surefire-plugin就不会执行它们。

]]>
使用links folder安装Eclipse插件 http://zhk.me/1427.html Fri, 05 Jul 2019 11:10:54 +0000 http://zhk.me/?p=1427 Read More »]]> 为了给Eclipse扩展功能,经常需要安装些插件,例如SonarLint等。工作环境中没有网,需要从网上下载一个plugin压缩包传到工作环境中,然后手动安装。以前的安装方法是:

  • 要么直接把解压后的plugins和features文件直接复制到Eclipse对应的plugins和features里。
  • 要么打开Eclipse –> Install New Software… –> Add… –> Local…。

这两种方法都是侵入式的,如果一个插件不想要了,很难卸载(需要慢慢的找,而且很难找的全)。

使用links安装插件

1 创建插件目录

插件的plugins和features必须在eclipse文件夹里。以SonarLint为例。

  1. 创建SonarLint/eclipse/文件夹。
  2. 把SonarLint的plugins和features都copy到SonarLint/eclipse/里。

2. 在Eclipse安装目录下创建links folder

mkdir ${ECLIPSE_HOME}/links

3. 在links folder里给插件创建link 文件

每个插件对应一个link文件,文件内容“path=/plugin/path/SonarLint/”。

  • link文件的文件名和扩展名是任意的,如myplugin.txt, another-plugin。
  • link文件中如果有多行”path=/plugin/path”,最后一个生效。
  • 插件目录也可以是相对路径。

我创建了pydev, sonarlint, theme这三个插件的link。

image

4. 总结

这样做的好处是:

  1. 卸载插件只需要在对应的link文件前加个‘#’,其实可以为任何字符,扰乱path这个key。
  2. 安装新的Eclipse,不用重新安装插件,把links folder复制过来就可以了。

参考:

https://blog.csdn.net/bear110/article/details/873667

]]>