7 重构、测试、调试
7.1 为改善可读性和灵活性重构代码
7.1.1 改善代码的可读性
跟之前的版本相比较,Java8的新特性也可以帮助提升代码的可读性
(1)使用java8,可以减少冗长的代码,让代码更容易理解
(2)通过Stream和方法引用,你的代码将变得更直观
以下介绍三种简单的重构
- 重构代码,用lamdba表达式重构匿名类
- 用方法引用重构Lamdba表达式
- 用StreamApi重构命令式的数据处理
7.1.2 从匿名类到Lamdba表达式
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("你是猪");
}
};
runnable.run();
Runnable runnable1 = () ->{
System.out.println("你是1");
};
runnable1.run();
应该注意一下两种情况
1.匿名类和Lamdba表达式中的this和super的含义不同。
在匿名类中的this是类自身,但是在Lamdba中是包含类
2.匿名类可以屏蔽包含类的变量,而Lamdba表达式不能(会导致变异错误),比如以下代码
int a= 10;
Runnable runnable1 = () ->{
int a = 2;
System.out.println("你是1");
};
runnable1.run();
7.1.3 从Lamdba表达式到方法引用的转换
简单来说就是Lamdba的返回体封装成方法直接调用
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel =
menu.stream()
.collect(
groupingBy(dish -> {
if (dish.getCalories() <= 400) return CaloricLevel.DIET;
else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL;
else return CaloricLevel.FAT;
}));
public static void test(List<Dish> menu){
Map<CaloricLevel, List<Dish>> dishesByCaloricLevel =
menu.stream()
.collect(
groupingBy(Task::getLevel));
}
public static CaloricLevel getLevel(Dish dish){
if (dish.getCalories() <= 400) {
return CaloricLevel.DIET;
}
else if (dish.getCalories() <= 700) {
return CaloricLevel.NORMAL;
}
else {
return CaloricLevel.FAT;
}
}
7.1.4 从命令式的数据处理切换到Stream
7.1.5 增加代码的灵活性
1.采用函数接口
2.有条件的延迟执行
3.环绕执行
/**
* 函数式接口
*/
@FunctionalInterface
public interface BufferedReaderProcessor {
String process(BufferedReader bufferedReader) throws Exception;
}
// 现在你就可以把这个接口作为新的processFile方法的参数了:
public static String processFile(BufferedReaderProcessor bufferedReaderProcessor){
try {
BufferedReader bufferedReader = new BufferedReader(new FileReader("E:\\learn\\java8-master\\java8\\src\\main\\resources\\data.txt"));
return bufferedReaderProcessor.process(bufferedReader);
}catch (Exception ex){
System.out.println("异常");
}
return null;
}
String twoLine = processFile((BufferedReader br)-> br.readLine() + br.readLine());
7.2 使用Lamdba重构面向对象的设计模式
- 策略模式
- 模板方法
- 观察者模式
- 责任链模式
- 工厂模式
我们通过Lamdba来解决上述设计模式所要解决的问题
7.2.1 策略模式
使用Lamdba表达式
package com.java.lamdba.seven;
public interface ValidationStrategy {
boolean execute(String s);
public static void main(String[] args) {
Validator numericValidator = new Validator(new IsNumeric());
boolean b1 = numericValidator.validate("aaaa");
Validator lowerCaseValidator = new Validator(new IsAllLowerCase ());
boolean b2 = lowerCaseValidator.validate("bbbb");
Validator test = new Validator((String s) -> s.matches("[a-z]+"));
Validator test2 = new Validator((String s) -> s.matches("\\d+"));
test.validate("aaa");
test2.validate("asdasd");
}
}
class IsAllLowerCase implements ValidationStrategy {
@Override
public boolean execute(String s){
return s.matches("[a-z]+");
}
}
class IsNumeric implements ValidationStrategy {
@Override
public boolean execute(String s){
return s.matches("\\d+");
}
}
class Validator{
private final ValidationStrategy strategy;
public Validator(ValidationStrategy v){
this.strategy = v;
}
public boolean validate(String s){
return strategy.execute(s);
}
}
7.2.2 模板方法
abstract class OnlineBanking {
public void processCustomer(int id){
Customer c = Database.getCustomerWithId(id);
makeCustomerHappy(c);
}
abstract void makeCustomerHappy(Customer c);
}
之前模板方法的重构
package com.learn.designmode.mode.template.demo;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* 账户类
*/
public abstract class Account {
/** 基本方法-具体方法
* @param account
* @param password
* @return
*/
public boolean validate(String account,String password){
System.out.println("账号" + account);
System.out.println("密码" + password);
if ("张无忌".equals(account) && "123456".equals(password)){
return true;
}else {
return false;
}
}
/**
* 基本方法-抽象方法
*/
public abstract void calculateInterest();
public void display(){
System.out.println("显示利息");
}
/** 模板方法
* @param account
* @param password
*/
public void handle(String account,String password){
if (!validate(account,password)){
System.out.println("账号密码错误");
return;
}
calculateInterest();
display();
}
/** 模板方法(使用Lamdba重构)
* @param account
* @param password
*/
public void handle(String account, String password, Supplier supplier){
if (!validate(account,password)){
System.out.println("账号密码错误");
return;
}
// calculateInterest 替换成Lamdba
//calculateInterest();
supplier.get();
display();
}
}
package com.learn.designmode.mode.template.demo;
import com.learn.designmode.utils.XMLUtil;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
public class Client {
public static void main(String[] args) throws SAXException, IllegalAccessException, IOException, InstantiationException, ParserConfigurationException, ClassNotFoundException {
Account account = (Account) XMLUtil.getBean("template");
//account.handle("张无忌","123456");
new Account() {
@Override
public void calculateInterest() {
}
}.handle("张无忌","12345612312",() ->{
System.out.println("显示Lamdba");
return null;
});
}
}
7.2.3 观察者模式
使用Lamdba
package com.java.lamdba.seven;
import java.util.ArrayList;
import java.util.List;
public interface Observer {
void notify(String tweet);
public static void main(String[] args) {
Feed f = new Feed();
// 普通的注入观察者
f.registerObserver(new Guardian());
// 使用Lambda注入观察者
f.registerObserver((String tweet) -> {
if(tweet != null && tweet.contains("money")){
System.out.println("Breaking news in NY! " + tweet);
}
});
f.registerObserver((String tweet) -> {
if(tweet != null && tweet.contains("queen")){
System.out.println("Yet another news in London... " + tweet);
}
});
f.notifyObservers("The queen said her favourite book is Java 8 in Action!");
}
}
class NYTimes implements Observer{
@Override
public void notify(String tweet) {
if(tweet != null && tweet.contains("money")){
System.out.println("Breaking news in NY! " + tweet);
}
}
}
class Guardian implements Observer{
@Override
public void notify(String tweet) {
if(tweet != null && tweet.contains("queen")){
System.out.println("Yet another news in London... " + tweet);
}
}
}
class LeMonde implements Observer{
@Override
public void notify(String tweet) {
if(tweet != null && tweet.contains("wine")){
System.out.println("Today cheese, wine and news! " + tweet);
}
}
}
interface Subject{
void registerObserver(Observer o);
void notifyObservers(String tweet);
}
class Feed implements Subject{
private final List<Observer> observers = new ArrayList<>();
@Override
public void registerObserver(Observer o) {
this.observers.add(o);
}
@Override
public void notifyObservers(String tweet) {
observers.forEach(o -> o.notify(tweet));
}
}
7.2.4 责任链模式
package com.java.lamdba.seven;
public abstract class ProcessingObject<T> {
protected ProcessingObject<T> successor;
public void setSuccessor(ProcessingObject<T> successor){
this.successor = successor;
}
public T handle(T input){
T r = handleWork(input);
if(successor != null){
return successor.handle(r);
}
return r;
}
abstract protected T handleWork(T input);
}
使用Lamdba表达式
package com.java.lamdba.seven;
import java.util.function.Function;
import java.util.function.UnaryOperator;
public abstract class ProcessingObject<T> {
public static void main(String[] args) {
ProcessingObject<String> p1 = new HeaderTextProcessing();
ProcessingObject<String> p2 = new SpellCheckerProcessing();
p1.setSuccessor(p2);
String result = p1.handle("Aren't labdas really sexy?!!");
System.out.println(result);
UnaryOperator<String> headerProcessing =
(String text) -> "From Raoul, Mario and Alan: " + text;
UnaryOperator<String> spellCheckerProcessing =
(String text) -> text.replaceAll("labda", "lambda");
Function<String, String> pipeline =
headerProcessing.andThen(spellCheckerProcessing);
String result2 = pipeline.apply("Aren't labdas really sexy?!!");
}
protected ProcessingObject<T> successor;
public void setSuccessor(ProcessingObject<T> successor){
this.successor = successor;
}
public T handle(T input){
T r = handleWork(input);
if(successor != null){
return successor.handle(r);
}
return r;
}
abstract protected T handleWork(T input);
}
class HeaderTextProcessing extends ProcessingObject<String> {
@Override
public String handleWork(String text){
return "From Raoul, Mario and Alan: " + text;
}
}
class SpellCheckerProcessing extends ProcessingObject<String> {
@Override
public String handleWork(String text){
return text.replaceAll("labda", "lambda");
}
}
7.2.5 工厂模式
package com.java.lamdba.seven;
public class ProductFactory {
public static Product createProduct(String name){
switch(name){
case "loan": return new Loan();
case "stock": return new Stock();
case "bond": return new Bond();
default: throw new RuntimeException("No such product " + name);
}
}
}
class Product{
}
class Loan extends Product{
}
class Stock extends Product{
}
class Bond extends Product{
}
使用Lamdba
package com.java.lamdba.seven;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class ProductFactory {
final static Map<String, Supplier<Product>> map = new HashMap<>();
static {
map.put("loan", Loan::new);
map.put("stock", Stock::new);
map.put("bond", Bond::new);
}
public static void main(String[] args) {
Supplier<Product> loanSupplier = Loan::new;
Loan loan = (Loan) loanSupplier.get();
System.out.println("aa");
}
public static Product createProduct(String name){
switch(name){
case "loan": return new Loan();
case "stock": return new Stock();
case "bond": return new Bond();
default: throw new RuntimeException("No such product " + name);
}
}
}
class Product{
}
class Loan extends Product{
}
class Stock extends Product{
}
class Bond extends Product{
}
7.3 测试Lamdba表达式
package com.java.lamdba.seven;
import org.junit.Test;
import static junit.framework.TestCase.assertEquals;
public class Point {
private final int x;
private final int y;
private Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
public Point moveRightBy(int x){
return new Point(this.x + x, this.y);
}
@Test
public void testMoveRightBy() throws Exception {
Point p1 = new Point(5, 5);
Point p2 = p1.moveRightBy(10);
assertEquals(15, p2.getX());
assertEquals(5, p2.getY());
}
}
7.3.1 测试可见的Lamdba函数行为
public final static Comparator<Point> compareByXAndThenY =
comparing(Point::getX).thenComparing(Point::getY);
@Test
public void testComparingTwoPoints() throws Exception {
Point p1 = new Point(10, 15);
Point p2 = new Point(10, 20);
int result = Point.compareByXAndThenY.compare(p1 , p2);
assertEquals(-1, result);
}
7.3.2 测试使用Lamdba的方法的行为
public static List<Point> moveAllPointsRightBy(List<Point> points, int x){
return points.stream()
.map(p -> new Point(p.getX() + x, p.getY()))
.collect(toList());
}
@Test
public void testMoveAllPointsRightBy() throws Exception{
List<Point> points =
Arrays.asList(new Point(5, 5), new Point(10, 5));
List<Point> expectedPoints =
Arrays.asList(new Point(15, 5), new Point(20, 5));
List<Point> newPoints = Point.moveAllPointsRightBy(points, 10);
assertEquals(expectedPoints, newPoints);
}
7.3.3 将复杂的Lamdba表达式分到不同的方法
7.3.4 高阶函数的测试
@Test
public void testFilter() throws Exception{
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> even = filter(numbers, (Integer i) -> i % 2 == 0);
List<Integer> smallerThanThree = filter(numbers, (Integer i) -> i < 3);
assertEquals(Arrays.asList(2, 4), even);
assertEquals(Arrays.asList(1, 2), smallerThanThree);
}
7.4 调试
调试有问题的代码时,程序员的兵器库里有两大老式武器,分别是:
(1)查看栈跟踪
(2)输出日志
7.4.1 查看栈跟踪
Lamdba表达式与栈跟踪
package com.java.lamdba.seven;
import java.util.Arrays;
import java.util.List;
public class Debugging{
public static void main(String[] args) {
List<Point> points = Arrays.asList(new Point(12, 2), null);
points.stream().map(p -> p.getX()).forEach(System.out::println);
}
}
7.4.2 使用日志调试
package com.java.lamdba.seven;
import java.util.ArrayList;
import java.util.List;
import static java.util.stream.Collectors.toList;
public class Test {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(1);
numbers.add(1);
numbers.add(1);
numbers.add(1);
numbers.add(1);
List<Integer> result =
numbers.stream()
.peek(x -> System.out.println("from stream: " + x))
.map(x -> x + 17)
.peek(x -> System.out.println("after map: " + x))
.filter(x -> x % 2 == 0)
.peek(x -> System.out.println("after filter: " + x))
.limit(3)
.peek(x -> System.out.println("after limit: " + x))
.collect(toList());
}
}