Welcome to 《A Little Java, A Few Patterns》笔记

Contents:

前言

tags:java, oop, design pattern, functional
category:java

Contents


学习编程不仅是学习一门编程语言的语法和方法,更需要学习如何 程序设计 。任何一本好的讲编程的书籍都需要教会人们程序设计。

在学校里教授的那些程序设计之类的课程,总会和一系列实际使用到的语言绑定在一起。

FelleisoenFriedman 这两位大师将函数式编程很自然地引导到众所周知的面向对象编程。他们将两种编程范式无缝地结合在一起,并且展示它们是如何很好地一起工作地。他们写的这本书就证明了函数式编程与面向对象编程并不冲突,反而能够很好的支持面向对象编程。

我并不惊讶于他们的成功(将两种编程范完美无缺地结合在一起)。因为我已经在好多年前从 Smalltalk 语言中了解到这一概念了。但不幸的是,这么多年过去了,它仍然是面向对象设计中的众多秘密之一。我很高兴 FelleisoenFriedman 最终揭开了它的面目。如是你是一名C++程序员,正在学习Java,并且对从未了解过函数式设计,那这本书对你来说就尤其有用。如果你了解过函数式设计,那么这本书会用一优雅的方式向你介绍Java中基于模式的编程。如果你不了解, FelleisoenFriedman 将会教会你一个强大的新的思维方法。

享受一下披萨吧。

序言

tags:java, oop, design pattern, functional
category:java

一门面向对象编程语言能够让程序员构造可复用的程序组件。其它程序员通过使用这些组件,能够快速地搭建新的大型程序和框架。在理想的状态下,程序员不需要修改任何已经存在的代码,只需要将它们简单地组装在一起然后添加一些新的代码即可。当然这些可复用的组件并不是天上掉下来的馅饼,它是有代价的。它需要一门经过良好设计的面向对象编程语言和严格的编程规范。

Java就是一门这样的语言。这本书会介绍它的面向对象元素:类、属性、方法、继承和接口。这些核心元素都有着简单的语法模型,能够帮助程序员表达它们。并且,Java还实现了内存自动管理,这更是让程序员们从思考机器的实现细节中解放出来,从而能够更专注于程序设计。

这本书的另外一个目的是向读者介绍 设计模式 。设计模式是能够加强代码复用的编程规范的关键所在。设计模式能够帮助程序员很好地组织面向对象组件,这样程序员就能够按照自己所希望的那样,实现整个计算流程了。更重要的是,设计模式有助于沟通程序组件的重要属性(这句不知道怎么意译,只能直译)。如果一个组件按照精确的公式化的模式实践(它的文档也一样),那么其他程序员就能够很容易地理解它的结构并且在自己的程序进行复用,甚至不需要了解该组件的源代码。

本书适用的读者

这本书主要的受众是那些愿意学习面向对象的本质以及设计模式的人,尤其是程序员、设计师和学生等等。读者需要有一些基本的编程经验。如果读者理解函数式编程的基本原理,那么他们将会从本书获得最大收益。了解函数式设计的最好办法是学一门使用Scheme(或者ML)语言进行教学的入门级计算机科学课程,但这并不是必需的。

本书未涉及的方面

Java在它的面向对象核心之上,提供了很多有用的特性及库。当然,这些额外的Java元素对于专业编程来说很重要,但对于本书的重要目标:

面向对象编程及设计模式的使用

来说却没有什么关系。正因为这个原因,本书不是一本完全的Java入门书籍。不过,掌握了本书内容的读者在学习了本书 Commencement 章节补充那些书以后,会很快成长为一名专业的Java程序员。

有关设计模式的书籍发展得非常快。所以对一本入门级的书来说,能够清楚涉及到的设计模式不是很多。但是我们用到的模式的简洁以及强大功能会鼓舞读者继续学习书后所提及的额外的有关设计模式的资料。

感谢

基本是感谢一堆人,以及每个人对本书做了哪些贡献,暂不翻译。

本书阅读指南

不要简单粗暴地过一遍本书。至少用心地读上7次。在读书时记得做标记或者写笔记,在本书的犄角旮旯会有诸多有用的提示。完整得过完每一个例子,而不是简单拿眼睛浏览一下。牢记一句箴言:先动脑,再动手。

本书形式是有关有趣的Java编程的对话。在你理解了书中的例子之后就动手实践它们,通过修改这些程序和例子来看看它们倒底是如何工作的。目前不幸的是大部分的Java实现都没有交互式命令行或者解释器。这就需要你这边做一些必要的重复性工作。接下来的章节会告诉你一些如何体验Java的提示。我们不会在本书里对设计模式给出任何正式的定义。我们相信读者你有这个能力能够给出你自己对设计模式的定义,这样你才能更好地理解并掌握它们,而不是通过我们直接灌输给你。但是你要确认你自己知道并理解了在本书大部分章节里给出的部分建议。

我们在书中使用一些 标记规定 来在不同层次帮助你理解程序。

注解

主要的规定就是通过字体来标识不同类型的单词。

该段下面基本就是告诉你各种类型的单词使用什么字体,

这个也不翻译了,看例子就知道。

本书的例子中出现食物名有两个原因。

  1. 食物比起那些抽象的符号来说更容易可视化

    (但是在你节食的时候读本书可不是一个好主意)。

    我们希望我们选择食物名能够有助读者理解本书中的例子和主题。

  2. 我们希望在读书时能够小分一下心。

    我们知道当你试图理解这些主题时多少有点沮丧,

    但是一丢丢分心多少能够帮助你远离沮丧的情绪。

你要准备开始阅读本书喽。祝你好运!我们希望你能够享受阅读本书。

Java小用

tags:java, oop
category:java

Contents


下面就是一些有关Java体验的提示:

  1. 在一个文件中给出类的完整层次。

  2. 对于每个命名不以上标D、V、I、M结尾的类,添加 toString 方法,并遵守以下规则:

    1. 如果一个类没有属性

      public String toString() {
          return "new " + getClass().getName() + "()";
      }
      
    2. 如果一个类只有一个属性,比如叫 x

      public String toString() {
          return "new " + getClass().getName() + "(" + x + ")";
      }
      
    3. 如果一个类有两个属性,比如叫 xy

      public String toString() {
          return "new " + getClass().getName() + "(" + x + ", " + y + ")";
      }
      
  3. 在文件的底部添加如下类:

    class Main {
        public static void main(String args[]) {
            DataType_or_Interface y = new ______;
            System.out.println( ... ... );
        }
    }
    

DataType_or_Interface y = new ______; 是用来创建你想尝试的对象。

System.out.println( ... ... ); 是用来填写你想尝试的表达式。

比如,你想尝试第2章定义的 ManhattanPtdistanceTo0 方法,你就可以添加如下代码到你文件的最后:

public class Main{
    public static void main(String args[]) {
        PointD y = new ManhattanPt(2, 8);
        System.out.println(y.distanceTo0());
    }
}

如果你想尝试多条表达式,就修改 y ,就像第10章里,

y._ _ _ _ _ _;
y._ _ _ _ _ _;
y._ _ _ _ _ _

替换成

y._ _ _ _ _ _ + "\n" +
y._ _ _ _ _ _ + "\n" +
y._ _ _ _ _ _

如果你想尝试第10章中定义的 PiemanM 的多个方法,那么你就将以下代码写在文件的最后面:

class Main {
    public static void main(String args[]) {
        PiemanI y = new PiemanM();
        System.out.println(
            y.addTop(new Anchovy()) + "\n" +
            y.addTop(new Anchovy()) + "\n" +
            y.substTop(new Tuna(), new Anchovy())
        );
    }
}
  1. 最后,编译文件并且执行 Main 类。

    小技巧

    按照上面的要求,保存的代码的文件名同时也要为 Main.java 。(因为Java会根据文件名来查找其文件内同名的类,再执行该类的main方法)

    然后使用 javac Main.java 来进行编译生成 Main.class 中间码文件。

    最后, java Main 来执行程序。

    建议大家上网搜索一下 Java helloworld 看一下相关教程就可以了。暂时不用深入学习 Java

Modern Toys

tags:java, oop
category:java

这一章节,作者通过一系列的对话,让读者了解到Java中基本类型(只介绍了int, boolean类型),然后引申到如何使用Java自定义类型(引用类型)。

类型是什么?

A type is a name for a collection of values

SeasoningD

自定义 SeasoningD 类型,以其它的四个子类型。

abstract class SeasoningD{} //调味品

class Salt extends SeasoningD{} //盐

class Pepper extends SeasoningD{} //胡椒粉

class Thyme extends SeasoningD{} //百里香

class Sage extends SeasoningD{} //鼠尾草

虽然四个子类型没有定义 构造函数 ,但是Java会自动添加一个默认的构造函数。

PointD

再自定义个 PointD 类型,和它的两个子类型。

abstract class PointD{} //坐标

class CartesianPt extends PointD{ //笛卡尔坐标
    int x;
    int y;
    CartesianPt(int _x, int _y){
        x = _x;
        y = _y;
    }
}

class ManhattanPt extends PointD{ //曼哈顿坐标
    int x;
    int y;
    ManhattanPt(int _x, int _y){
        x = _x;
        y = _y;
    }
}

PointD的两个子类型就手动添加了构造函数,因为它们需要有额外的属性传入构造函数。

当使用 new 关键字时, Java会通过调用类的构造函数来生成其对应的实例。

对抽象类直接使用 new 关键字是不行的,因为抽象类是一个未完全定义的类,无法实例化。

NumD

再定义个 NumD 类型,和它的两个子类型。

abstract class NumD{}

class Zero extends NumD{}

class OneMoreThan extends NumD{
    NumD predecessor;
    OneMoreThan(NumD _d){
        predecessor = _d;
    }
}

使用这两个子类型,就可以表示一个整数系统。

  1. Zero 表示 0
  2. new OneMoreThan(new Zero()) 表示 1
  3. new OneMoreThan(new OneMoreThan(new Zero())) 表示 2
  4. ... ...

小技巧

上面的概念和 Church encoding 很类似了。

abstractclassextends 各代表什么?

`abstract` 定义类型

`class` 定义子类型

`extends` 将以上两者联系起来

第一条建议

When specifying a collection of data,

use abstract classes for datatypes and

extended classes for variants.

LayerD

abstract class LayerD{}

class Base extends LayerD{
    Object o;
    Base(Object _o){
        o = _o;
    }
}

class Slice extends LayerD{
    LayerD l;
    Slice(LayerD _l){
        l = _l;
    }
}

书中通过一系列对话和示例来揭示自定义类型与Java提供的基本类型的不同之处,给读者一个基本印象:基本类型不能直接作用于自定义类型,而是将之先转换为类似自定义类型的形式,然后才能使用。

接下来的章节会其进行进一步的揭示。

小技巧

Java不能说是完全的面向对象。为了性能考虑,Java的基本类型并非对象。

如果需要将之转换为对象,需要使用Java提供的包装类才行。

如果想深入了解:请Google Java 基本类型 引用类型

Methods to Our Madness

tags:java, oop
category:java

上一章讲解了如何在Java中的定义类型。

这一章主要讲如何向这些类型添加方法。

PointD

abstract class PointD{
    abstract int distanceTo0();
}

class CartesianPt extends PointD{ //笛卡尔坐标
    int x;
    int y;
    CartesianPt(int _x, int _y){
        x = _x;
        y = _y;
    }
    int distanceTo0(){
        return (int)Math.sqrt(x * x + y * y);
    }
}

class ManhattanPt extends PointD{ //曼哈顿坐标
    int x;
    int y;
    ManhattanPt(int _x, int _y){
        x = _x;
        y = _y;
    }
    int distanceTo0(){
        return x + y;
    }
}

当子类型(具体类)从类型(抽象类)继承时,需要同时实现抽象类中的抽象方法。

ShishD

// 书上的例子中各个类一层层的套在一起,可以理解成一个烤串
abstract class ShishD { //羊肉串
    abstract boolean onlyOnions(); //烤串上是不是只有洋葱
    abstract boolean isVegetarian(); //烤串上是不是全是蔬菜
}

class Skewer extends ShishD { //串,烤肉叉子
    boolean onlyOnions(){
        return true;
    }

    boolean isVegetarian(){
        return true;
    }
}

class Onion extends ShishD { //洋葱
    ShishD s;
    Onion(ShishD _s) {
        s = _s;
    }

    boolean onlyOnions(){
        return s.onlyOnions();
    }

    boolean isVegetarian(){
        return s.isVegetarian();
    }
}

class Lamb extends ShishD { //羔羊肉
    ShishD s;
    Lamb(ShishD _s) {
        s = _s;
    }

    boolean onlyOnions(){
        return false;
    }

    boolean isVegetarian(){
        return false;
    }
}

class Tomato extends ShishD { //西红柿
    ShishD s;
    Tomato(ShishD _s) {
        s = _s;
    }

    boolean onlyOnions(){
        return false;
    }

    boolean isVegetarian(){
        return s.isVegetarian();
    }
}

第二条建议

When writing a function over a datatype,

place a method in each of the variants that make up the datatype.

If a field of a variant belongs to the same datatype,

the method may call the corresponding method of the field in

computing the function.

KebabD

abstract class KebabD { //烤肉
    abstract boolean isVeggie(); //是否以纯蔬菜为辅料的烤肉
    abstract Object whatHolder(); //烤肉的摆放工具是什么
}

class Holder extends KebabD { //烤肉摆放工具(意译)
    Object o;
    Holder (Object _o) {
        o = _o;
    }
    boolean isVeggie(){
        return true;
    }
    Object whatHolder(){
        return o;
    }
}

class Shallot extends KebabD { //葱
    KebabD k;
    Shallot(KebabD _k) {
        k = _k;
    }
    boolean isVeggie(){
        return k.isVeggie();
    }
    Object whatHolder(){
        return k.whatHolder();
    }
}

class Shrimp extends KebabD { //小虾
    KebabD k;
    Shrimp(KebabD _k) {
        k = _k;
    }
    boolean isVeggie(){
        return false;
    }
    Object whatHolder(){
        return k.whatHolder();
    }
}

class Radish extends KebabD { //萝卜
    KebabD k;
    Radish(KebabD _k) {
        k = _k;
    }
    boolean isVeggie(){
        return k.isVeggie();
    }
    Object whatHolder(){
        return k.whatHolder();
    }
}

class Pepper extends KebabD { //胡椒粉
    KebabD k;
    Pepper(KebabD _k) {
        k = _k;
    }
    boolean isVeggie(){
        return k.isVeggie();
    }
    Object whatHolder(){
        return k.whatHolder();
    }
}

class Zucchini extends KebabD { //西葫芦
    KebabD k;
    Zucchini(KebabD _k) {
        k = _k;
    }
    boolean isVeggie(){
        return k.isVeggie();
    }
    Object whatHolder(){
        return k.whatHolder();
    }
}

定义一下烤肉摆放的工具。

大致分成两种:

  • 一种是将烤肉串起来的工具

    abstract class RodD{} //杆,用于将烤肉串起来
    
    class Dagger extends RodD{} //匕首
    
    class Sabre extends RodD{} //军刀
    
    class Sword extends RodD{} //剑
    
  • 一种将烤肉平铺的工具。

    abstract class PlateD{} //盘子
    
    class Gold extends PlateD{} //金盘子
    
    class Silver extends PlateD{} //银盘子
    
    class Brass extends PlateD{} //黄铜盘子
    
    class Copper extends PlateD{} //镀铜盘子
    
    class Wood extends PlateD{} //木盘子
    

PointD

abstract class PointD{
    abstract int distanceTo0();
}

class CartesianPt extends PointD{ //笛卡尔坐标
    int x;
    int y;
    CartesianPt(int _x, int _y){
        x = _x;
        y = _y;
    }
    int distanceTo0(){
        return (int)Math.sqrt(x * x + y * y);
    }
    boolean closerTo0(CartesianPt p){
        return distanceTo0() <= p.distanceTo0();
    }
}

class ManhattanPt extends PointD{ //曼哈顿坐标
    int x;
    int y;
    ManhattanPt(int _x, int _y){
        x = _x;
        y = _y;
    }
    int distanceTo0(){
        return x + y;
    }
    boolean closerTo0(ManhattanPt p){
        return distanceTo0() <= p.distanceTo0();
    }
}

抽取变体类型中公共的部分到抽象类型中。

abstract class PointD{
    int x;
    int y;
    PointD(int _x, int _y){
        x = _x;
        y = _y;
    }
    abstract int distanceTo0();
    boolean closerTo0(PointD p){
        return distanceTo0() <= p.distanceTo0();
    }
}

class CartesianPt extends PointD{ //笛卡尔坐标
    CartesianPt(int _x, int _y){
        super(_x, _y);
    }
    int distanceTo0(){
        return (int)Math.sqrt(x * x + y * y);
    }
}

class ManhattanPt extends PointD{ //曼哈顿坐标
    ManhattanPt(int _x, int _y){
        super(_x, _y);
    }
    int distanceTo0(){
        return x + y;
    }
}

What’s New?

tags:java, oop
category:java

PizzaD

abstract class PizzaD { //比萨饼
    abstract PizzaD remA(); //去除比萨饼中的凤尾鱼顶料(因为太咸了)
    abstract PizzaD topAwC(); //在凤尾鱼顶料上加上奶酪顶料(这样会盖住凤尾鱼的咸味)
    abstract PizzaD subAbC(); //将所有的凤尾鱼顶料换成奶酪顶料
}

class Crust extends PizzaD { //面包皮
    PizzaD subAbC(){
        return new Crust();
    }
    PizzaD topAwC(){
        return new Crust();
    }
    PizzaD subAbC(){
        return new Crust();
    }
}

// 下面是各种顶料
class Cheese extends PizzaD { //奶酪
    PizzaD p;
    Cheese (PizzaD _p) {
        p = _p;
    }
    PizzaD remA(){
        return new Cheese(p.remA());
    }
    PizzaD topAwC(){
        return new Cheese(p.topAwC());
    }
    PizzaD subAbC(){
        return new Cheese(p.subAbC());
    }
}

class Olive extends PizzaD { //橄榄
    PizzaD p;
    Olive (PizzaD _p) {
        p = _p;
    }
    PizzaD remA(){
        return new Olive(p.remA());
    }
    PizzaD topAwC(){
        return new Olive(p.topAwC());
    }
    PizzaD subAbC(){
        return new Olive(p.subAbC());
    }
}

class Anchovy extends PizzaD { //凤尾鱼
    PizzaD p;
    Anchovy (PizzaD _p) {
        p = _p;
    }
    PizzaD remA(){
        return p.remA();
    }
    PizzaD topAwC(){
        return new Cheese(new Anchovy(p.topAwC()));
    }
    PizzaD subAbC(){
        return new Cheese(p.subAbC());
    }
}

class Sausage extends PizzaD { //香肠
    PizzaD p;
    Sausage (PizzaD _p) {
        p = _p;
    }
    PizzaD remA(){
        return new Sausage(p.remA());
    }
    PizzaD topAwC(){
        return new Sausage(p.topAwC());
    }
    PizzaD subAbC(){
        return new Sausage(p.subAbC());
    }
}

如果想要在比萨饼上面添加额外的顶料怎么办?

很简单,再从 PizzaD 扩展出一个新的子类型就可以了。

class Spinach extends PizzaD { //菠菜
    PizzaD p;
    Spinach (PizzaD _p) {
        p = _p;
    }
    PizzaD remA(){
        return new Spinach(p.remA());
    }
    PizzaD topAwC(){
        return new Spinach(p.topAwC());
    }
    PizzaD subAbC(){
        return new Spinach(p.subAbC());
    }
}

但是每添加一个新的变体类型都要加上三个方法,好累的说。

有什么比较好的办法解决这个问题呢?

下一章节给你答案。

第三条建议

When writing a function that returns values of a datatype,

use new to create these values.

Objects Are People, Too

tags:java, oop
category:java

PieD

abstract class PieD { //馅饼,派
    RemAV raFn = new RemAV();
    RemFishV rfFn = new RemFishV();
    abstract PieD remA();
    abstract PieD remFish(FishD f);
}

class Bot extends PieD { //底料
    PieD remA() {
        return raFn.forBot();
    }
    Pied remFish(FishD f) {
        return rfFn.forBot(f);
    }
}

class Top extends PieD { //顶料
    Object t;
    PieD r;
    Top(Object _t, PieD _r) {
        t = _t,
        r = _r,
    }
    PieD remA() {
        return raFn.forTop(t, r);
    }
    PieD remFish(FishD f) {
        return rfFn.forTop(t, r, f);
    }
}

FishD

abstract class FishD {}

class Anchovy extends FishD { //凤尾鱼
    public boolean equals(Object o) {
        return (o instanceof Anchovy);
    }
}

class Salmon extends FishD { //鲑鱼
    public boolean equals(Object o) {
        return (o instanceof Salmon);
    }
}

class Tuna extends FishD { //金枪鱼
    public boolean equals(Object o) {
        return (o instanceof Tuna);
    }
}
// 删除凤尾鱼的方法
class RemAV {
    PieD forBot() {
        return new Bot();
    }
    PieD forTop(Object t, PieD r) {
        if (new Anchovy().equals(t))
            return r.remA();
        else
            return new Top(t, r.remA());
    }
}

// 删除指定鱼的方法,相当在 `RemAV` 的概念上再抽象一层
class RemFishV {
    PieD forBot(FishD f) {
        return new Bot();
    }
    PieD forTop(Object t, PieD r, FishD f) {
        if (f.equals(t))
            return r.remFish(f);
        else
            return new Top(t, r.remFish(f));
    }
}

// 删除指定整数的方法
class RemIntV {
    PieD forBot(Integer i) {
        return new Bot();
    }
    PieD forTop(Object t, PieD r, Integer i) {
        if (i.equals(t))
            return r.remInt(i);
        else
            return new Top(t, r.remInt(i));
    }
}

RemFishVRemIntV 的整个逻辑很类似么,那么将它们重新抽象一下?

class RemV {
    PieD forBot(Object o) {
        return new Bot();
    }
    PieD forTop(Object t, PieD r, Object o) {
        if (o.equals(t))
            return r.rem(o);
        else
            return new Top(t, r.rem(o));
    }
}

PieD 及它的变种类型 Bot Top 也要简单变化一下。

abstract class PieD {
    RemV remFn = new RemV();
    abstract PieD rem(Object o);
}

class Bot extends PieD {
    PieD rem(Object o){
        return remFn.forBot(o);
    }
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, PieD _r){
        t = _t;
        r = _r;
    }
    PieD rem(Object o){
        return remFn.forTop(t, r, o);
    }
 }

现在的 Bot Top 在调用rem时,即可以传入 FishD 也可以传入 Integer 了。

NumD

abstract class NumD {}

class OneMoreThan extends NumD {
    NumD predecessor;
    OneMoreThan(NumD _p) {
        predecessor = _p;
    }
    public boolean equals(Object o) {
        if (o instanceof OneMoreThan)
            return predecessor.equals(
                ((OneMoreThan)o).predecessor
            ),
        else
            return false;
    }
}

class Zero extends NumD {
    public boolean equals(Object o) {
        return (o instanceof Zero);
    }
}
class SubstFishV {
    PieD forBot(FishD n, FishD o) {
        return new Bot();
    }
    PieD forTop (Object t, PieD r, FishD n, FishD o) {
        if (o.equals(t))
            return new Top(n, r.substFish(n, o));
        else
            return new Top(t, r.substFish(n, 0));
    }
}

class SubstIntV {
    PieD forBot(Integer n, Integer o) {
        return new Bot();
    }
    PieD forTop (Object t, PieD r, Integer n, Integer o) {
        if (o.equals(t))
            return new Top(n, r.substInt(n, o));
        else
            return new Top(t, r.substInt(n, 0));
    }
}

class SubstV {
    PieD forBot(Object n, Object o) {
        return new Bot();
    }
    PieD forTop (Object t, PieD r, Object n, Object o) {
        if (o.equals(t))
            return new Top(n, r.subst(n, o));
        else
            return new Top(t, r.subst(n, 0));
    }
}

SubstVRemV 的做法是一样。

再整理一下 PieD

abstract class PieD {
    RemV remFn = new RemV();
    SubsbV substFn = new SubstV();
    abstract PieD rem(Object o);
    abstract PieD subst(Object n, Object o);
}

class Bot extends PieD {
    PieD rem(Object o){
        return remFn.forBot(o);
    }
    PieD subst(Object n, Object o){
        return substFn.forBot(n, o)
    }
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, PieD _r){
        t = _t;
        r = _r;
    }
    PieD rem(Object o){
        return remFn.forTop(t, r, o);
    }
    PieD subst(Object n, Object o){
        return substFn.forTop(n, o)
    }
}

Boring Protocols

tags:java, oop
category:java

Contents

之前5章看起来还真是 Boring

这一章的名称中虽然有 Boring ,但是内容却是很有趣的。

小技巧

这里的 Protocols 我觉得应该指的是 Interface 的意思。


本章接着上一章节的最后代码继续讲解。

这次将 remFn substFn 放入到参数的位置。

abstract class PieD {
    abstract PieD rem(RemV remFn, Object o);
    abstract PieD subst(SubstV substFn, Object n, Object o);
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, PieD _r) {
        t = _t;
        r = _r;
    }
    PieD rem(RemV remFn, Object o) {
        return remFn.forTop(t, r, o);
    }
    PieD subst(SubstV substFn, Object n, Object o) {
        return sbustFn.forTop(t, r, n, o);
    }
}

class Bot extends PieD {
    Object t;
    PieD r;
    Bot(Object _t, PieD _r) {
        t = _t;
        r = _r;
    }
    PieD rem(RemV remFn, Object o) {
        return remFn.forBot(t, r, o);
    }
    PieD subst(SubstV substFn, Object n, Object o) {
        return sbustFn.forBot(t, r, n, o);
    }
}

引入 this 关键字,指代访问者本身,同步修改对应的访问者类。

class RemV {
    PieD forBot(Object o){
        return new Bot();
    }
    PieD forTop(Object t, PieD r, Object o){
        if (o.equals(t))
            return r.rem(this, o);
        else
            return new Top(t, r.rem(this, o));
    }
}

class SubstV {
    PieD forBot(Object n, Object o){
        return new Bot();
    }
    PieD forTop(Object t, PieD r, Object n, Object o){
        if (o.equals(t))
            return new Top(n, r.subst(this, n, o));
        else
            return new Top(t, r.subst(this, n, o));
    }
}

再修改访问者类,将更多的参数传入到访问者类中。

class RemV {
    Object o;
    RemV(Object _o) {
        o = _o;
    }
    PieD forBot(Object o){
        return new Bot();
    }
    PieD forTop(Object t, PieD r){
        if (o.equals(t))
            return r.rem(this);
        else
            return new Top(t, r.rem(this));
    }
}

class SubstV {
    Object n;
    Object o;
    SubstV(Object _n, Object _o){
        n = _n;
        o = _o;
    }

    PieD forBot(Object n, Object o){
        return new Bot();
    }
    PieD forTop(Object t, PieD r){
        if (o.equals(t))
            return new Top(n, r.subst(this));
        else
            return new Top(t, r.subst(this));
    }
}

上面的形式就有点函数式编程里面的 闭包 的意味了。

好了,根据上面修改后的 SubstV ,重新修改一下 PieD 及其 BotTop

abstract class PieD {
    abstract PieD rem(RemV remFn);
    abstract PieD subst(SubstV substFn);
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, PieD _r) {
        t = _t;
        r = _r;
    }
    PieD rem(RemV remFn) {
        return remFn.forTop(t, r);
    }
    PieD subst(SubstV substFn) {
        return sbustFn.forTop(t, r);
    }
}

class Bot extends PieD {
    Object t;
    PieD r;
    Bot(Object _t, PieD _r) {
        t = _t;
        r = _r;
    }
    PieD rem(RemV remFn) {
        return remFn.forBot();
    }
    PieD subst(SubstV substFn) {
        return sbustFn.forBot();
    }
}

TopBot 类中, remsubst 的代码都很类似,

所以我们可以进一步抽象。

警告

前方高能预警,高潮就要到来了。

// abstract class PieVisitorD {
//     abstract PieD forBot();
//     abstract PieD forTop(Object t, PieD r);
// }

interface PieVisitorI {
    PieD forBot();
    PieD forTop(Object t, PieD r);
}

class RemV implements PieVisitorI {
    Object o;
    RemV(Object _o) {
        o = _o;
    }
    public PieD forBot() {
        return new Bot();
    }
    public PieD forTop(Object t, PieD r) {
        if (o.equals(t))
            return r.accept(this);
        else
            return new Top(t, r.accept(this));
    }
}

class SbustV implements PieVisitorI {
    Object n;
    Object o;
    SubstV(Object _n, Object _o) {
        n = _n;
        o = _o;
    }
    public PieD forBot() {
        return new Bot();
    }
    public PieD fotTop(Object t, PieD r) {
        if (o.equals(t))
            return new Top(n, r.accept(this));
        else
            return new Top(t, r.accept(this));
    }
}

同步修改 PieD

abstract class PieD {
    abstract PieD accept(PieVisitorI ask);
}

class Bot extends PieD {
    PieD accept(PieVisitorI ask) {
        return ask.forBot();
    }
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, PieD _r) {
        t = _t;
        r = _r;
    }
    PieD accept(PieVisitorI ask) {
        return ask.forTop(t, r);
    }
}
// 有限制次数的替换
class LtdSubstV implements PieVisitorI {
    int c;
    Object n;
    Object o;
    LtdSubstV(int _c, Object _n, Object _o) {
        c = _c;
        n = _n;
        o = _o;
    }
    public PieD forBot() {
        return new Bot();
    }
    public PieD forTop(Object t, PieD r) {
        if (c == 0)
            return new Top(t, r);
        else
            if (o.equals(t))
                return new Top(n, r.accept(LtdSubstV(c-1, n, o)));
            else
                return new Top(t, r.accept(this));
    }
}

小技巧

第六条建议

When the additional consumed values

change for a self-referenced use of a

visitor, don’t forget to create a new visitor.

Oh My!

tags:java, oop
category:java

Contents


abstract class FruitD {} // 水果

class Peach extends FruitD {  //桃
    public boolean equals(Object o) {
        return (o instanceof Peach);
    }
}

class Apple extends FruitD { //苹果
    public boolean equals(Object o) {
        return (o instanceof Apple);
    }
}

class Pear extends FruitD { //梨
    public boolean equals(Object o) {
        return (o instanceof Pear);
    }
}

class Lemon extends FruitD { //柠檬
    public boolean equals(Object o) {
        return (o instanceof Lemon);
    }
}

class Fig extends FruitD { //无花果
    public boolean equals(Object o) {
        return (o instanceof Fig);
    }
}
abstract class TreeD { //树
    abstract boolean accept(bTreeVisitorI ask);
    abstract int accept(iTreeVisitorI ask);
    abstract TreeD accept(tTreeVisitorI ask);
}

class Bud extends TreeD { //芽
    boolean accept(bTreeVisitorI ask) {
        return ask.forBud();
    }
    int accept(iTreeVisitorI ask) {
        return ask.forBud();
    }
    TreeD accept(tTreeVisitorI ask) {
        return ask.forBud();
    }
}

class Flat extends TreeD { //平顶
    FruitD f;
    TreeD t;
    Flat(FruitD _f, TreeD _t) {
        f = _f;
        t = _t;
    }
    boolean accept(bTreeVisitorI ask) {
        return ask.forFlat(f, t);
    }
    int accept(iTreeVisitorI ask) {
        return ask.forFlat(f, t);
    }
    TreeD accept(tTreeVisitorI ask) {
        return ask.forFlat(f, t);
    }
}

class Split extends TreeD { //分枝
    TreeD l;
    TreeD r;
    Split(Treed _l, TreeD _r) {
        l = _l;
        r = _r;
    }
    boolean accept(bTreeVisitorI ask) {
        return ask.forSplit(l, r);
    }
    int accept(iTreeVisitorI ask) {
        return ask.forSplit(l, r);
    }
    TreeD accept(tTreeVisitorI ask) {
        return ask.forFlat(l, r);
    }
}
interface bTreeVisitorI {
    boolean forBud();
    boolean forFlat(FruitD f, TreeD t);
    boolean forSplit(TreeD l, TreeD r);
}

class bIsFlatV implements bTreeVisitorI {
    public boolean forBud() {
        return true;
    }
    public boolean forFlat(FruitD f, TreeD t) {
        return t.accept(this);
    }
    public boolean forSplit(TreeD l, TreeD r) {
        return false;
    }
}

class bIsSplitV implements bTreeVisitorI {
    public boolean forBud() {
        return true;
    }
    public boolean forFlat(FruitD f, TreeD t) {
        return false;
    }
    public boolean forSplit(TreeD l, TreeD r) {
        return l.accept(this) && r.accept(this);
    }
}

class bHasFruitV implements bTreeVisitorI {
    public boolean forBud() {
        return false;
    }
    public boolean forFlat(FruitD f, TreeD t) {
        return true;
    }
    public boolean forSplit(TreeD l, TreeD r) {
        return l.accept(this) || r.accept(this);
    }
}
interface iTreeVisitorI {
    int forBud();
    int forFlat(FruitD f, TreeD t);
    int forSplit(TreeD l, TreeD r);
}

class iHeightV implements iTreeVisitorI {
    public int forBud() {
        return 0;
    }
    public int forFlat(FruitD f, TreeD t) {
        return t.accept(this) + 1;
    }
    public int forSplit(TreeD l, TreeD r) {
        return (l.accept(this) |_| r.accept(this)) + 1;
    }
}

class iOccursV implements iTreeVisitorI {
    FruitD a;
    iOccursV(FruitD _a) {
        a = _a;
    }
    public int forBud() {
        return 0;
    }
    public int forFlat(FruitD f, TreeD t) {
        if (f.equals(a))
            return t.accept(this) + 1;
        else
            return t.accept(this);
    }
    public int forSplit(TreeD l, TreeD r) {
        return l.accept(this) + r.accept(this);
    }
}
interface tTreeVisitorI {
    TreeD forBud();
    TreeD forFlat(FruitD f, TreeD t);
    TreeD forSplit(TreeD l, TreeD r);
}

class tSubstV implements tTreeVisitorI {
    FruitD n;
    FruitD o;
    tSubstV(FruitD _n, FruitD _o) {
        n = _n;
        o = _o;
    }
    public TreeD forBud() {
        return new Bud();
    }
    public TreeD forFlat(FruitD f, TreeD t) {
        if (o.equals(f))
            return new Flat(n, t.accept(this));
        else
            return new Flat(f, t.accept(this));
    }
    public TreeD forSplit(TreeD l, TreeD r) {
        return new Split(l.accept(this), r.accept(this));
    }
}

上面的三个接口是不是有点繁琐?那么将它合并起来。

interface TreeVisitorI {
    Object forBud();
    Object forFlat(FruitD f, TreeD t);
    Object forSplit(TreeD l, TreeD r);
}

abstract class TreeD {
    abstract Object accept(TreeVisitorI ask);
}

class Bud extends TreeD {
    Object accept(TreeVisitorI ask) {
        return ask.forBud();
    }
}

class Flat extends TreeD {
    FruitD f;
    TreeD t;
    Flat(FruitD _f, TreeD _t) {
        f = _f;
        t = _t;
    }
    Object accept(TreeVisitorI ask) {
        return ask.forFlat(f, t);
    }
}

class Split extends TreeD {
    TreeD l;
    TreeD r;
    Split(Treed _l, TreeD _r) {
        l = _l;
        r = _r;
    }
    Object accept(TreeVisitorI ask) {
        return ask.forSplit(l, r);
    }
}

class IsFlatV implements TreeVisitorI {
    public Object forBud() {
        return new Boolean(true);
    }
    public Object forFlat(FruitD f, TreeD t) {
        return t.accept(this);
    }
    public Object forSplit(TreeD l, TreeD r) {
        return new Boolean(false);
    }
}

class bIsSplitV implements bTreeVisitorI {
    public boolean forBud() {
        return new Boolean(true);
    }
    public boolean forFlat(FruitD f, TreeD t) {
        return new Boolean(false);
    }
    public boolean forSplit(TreeD l, TreeD r) {
        if (((Boolean)(l.accept(this))).booleanValue())
            return r.accept(this);
        else:
            return new Boolean(false);
    }
}

小技巧

第七条建议

When designing visitor protocols for

many different types, create a unifying

protocol using Object .

但是这有一个不好的地方,如果返回的是Java的内置类型,

那内部在进行处理时,就要先进行转换,有时候甚至要令人发指的程度。

比如下面的代码:

class OccursV implements TreeVisitorI {
    FruitD a;
    iOccursV(FruitD _a) {
        a = _a;
    }
    public Object forBud() {
        return new Integer(0);
    }
    public Object forFlat(FruitD f, TreeD t) {
        if (f.equals(a))
            return new Integer(((Integer)(t.accept(this))).intValue() + 1);
        else
            return t.accept(this);
    }
    public int forSplit(TreeD l, TreeD r) {
        return new Integer(((Integer)(l.accept(this))).intValue()
                           +
                           ((Integer)(r.accept(this))).intValue());
    }
}

小技巧

难道只能这样?下面的章节给你答案。

Like Father, Like Son

tags:java, oop
category:java

这一章节主要讲到了继承,从章节题目也可以看出来: 父子。


上一章节的最后表明了一个问题:

由于自定义类型不支持运算,只能Java的内置类型有运算功能,

所以在进行运算时,需要先转换成内置类型然后运算完后,

再转换成特定的类型。

为了解决这个问题,就需要定义自定义类型的相关操作方法了。

连运算操作也进行面向对象化。

interface ExprVisitorI {
    Object forPlus(ExprD l, ExprD r); // 相加
    Object forDiff(ExprD l, ExprD r); // 相减
    Object forProd(ExprD l, ExprD r); // 相乘
    Object forConst(Object c); // 常量
}

abstract class ExprD {
    abstract Object accept(ExprVisitorI ask);
}

class Plus extends ExprD {
    ExprD l;
    ExprD r;
    Plus(ExprD _l, ExprD _r) {
        l = _l;
        r = _r;
    }
    Object accept(ExprVisitorI ask){
        return ask.forPlus(l, r);
    }
}

class Diff extends ExprD {
    ExprD l;
    ExprD r;
    Diff(ExprD _l, ExprD _r) {
        l = _l;
        r = _r;
    }
    Object accept(ExprVisitorI ask){
        return ask.forDiff(l, r);
    }
}

class Prod extends ExprD {
    ExprD l;
    ExprD r;
    Prod(ExprD _l, ExprD _r) {
        l = _l;
        r = _r;
    }
    Object accept(ExprVisitorI ask){
        return ask.forProd(l, r);
    }
}

class Const extends ExprD {
    Object c;
    Const(Object _c){
        c = _c;
    }
    Object accept(ExprVisitorI ask){
        return ask.forConst(c);
    }
}
class IntEvalV implements ExprVisitorI {
    public Object forPlus(ExprD l, ExprD r){
        return plus(l.accept(this), r.accept(this));
    }
    public Object forDiff(ExprD l, ExprD r){
        return diff(l.accept(this), r.accept(this));
    }
    public Object forProd(ExprD l, ExprD r){
        return prod(l.accept(this), r.accept(this));
    }
    public Object forConst(Object c){
        return c;
    }
    Object plus(Object l, Object r){
        return new Integer(((Integer)l).intValue()
                           +
                           ((Integer)r).intValue());
    }
    Object diff(Object l, Object r){
        return new Integer(((Integer)l).intValue()
                           -
                           ((Integer)r).intValue());
    }
    Object prod(Object l, Object r){
        return new Integer(((Integer)l).intValue()
                           *
                           ((Integer)r).intValue());
    }
}

再实现,Set(集合)类型的运算操作。

abstract class SetD {
    SetD add(Integer i){
        if (mem(i))
            return this;
        else
            return new Add(i, this);
    }
    abstract boolean mem(Integer i);
    abstract SetD plus(SetD s);
    abstract SetD diff(SetD s);
    abstract SetD prod(SetD s);
}

class Empty extends SetD {
    boolean mem(Integer i){
        return false;
    }
    SetD plus(SetD s){
        return s;
    }
    SetD diff(SetD s){
        return new Empty();
    }
    SetD prod(SetD s){
        return new Empty();
    }
}

class Add extends SetD {
    Integer i;
    SetD s;
    Add(Integer _i, SetD _s){
        i = _i;
        s = _s;
    }
    boolean mem(Integer n){
        if (i.equals(n))
            return true;
        else
            return s.mem(n);
    }
    SetD plus(SetD t){
        return s.plus(t.add(i));
    }
    SetD diff(SetD t){
        if (t.mem(i))
            return s.diff(t);
        else
            return s.diff(t).add(i);
    }
    SetD prod(SetD t){
        if (t.mem(i))
            return s.prod(t).add(i);
        else
            return s.prod(t);
    }
}

class SetEvalV extends IntEvalV {
    Object plus(Object l, Object r){
        return ((SetD)l).plus((SetD)r);
    }
    Object diff(Object l, Object r){
        return ((SetD)l).diff((SetD)r);
    }
    Object prod(Object l, Object r){
        return ((SetD)l).prod((SetD)r);
    }
}

SetEvalV 直接继承 IntEvalV 有点不合理,好,我们改一下。

再抽象一个基类出来,将两者的公共代码放在基类里,然后让两者继承该基类。

abstract class EvalD implements ExprVisitorI {
    public Object forPlus(ExprD l, ExprD r){
        return plus(l.accept(this), r.accept(this));
    }
    public Object forDiff(ExprD l, ExprD r){
        return diff(l.accept(this), r.accept(this));
    }
    public Object forProd(ExprD l, ExprD r){
        return prod(l.accept(this), r.accept(this));
    }
    public Object forConst(Object c){
        return c;
    }
    abstract Object plus(Object l, Object r);
    abstract Object diff(Object l, Object r);
    abstract Object prod(Object l, Object r);
}

class IntEvalV extends EvalD {
    Object plus(Object l, Object r){
        return new Integer(((Integer)l).intValue()
                           +
                           ((Integer)r).intValue());
    }
    Object diff(Object l, Object r){
        return new Integer(((Integer)l).intValue()
                           -
                           ((Integer)r).intValue());
    }
    Object prod(Object l, Object r){
        return new Integer(((Integer)l).intValue()
                           *
                           ((Integer)r).intValue());
    }
}

class SetEvalV extends EvalD {
    Object plus(Object l, Object r){
        return ((SetD)l).plus((SetD)r);
    }
    Object diff(Object l, Object r){
        return ((SetD)l).diff((SetD)r);
    }
    Object prod(Object l, Object r){
        return ((SetD)l).prod((SetD)r);
    }
}

还记得第6章的 SubstVLtdSubstV 么?

它们有很多相似的地方,能否采用上面的办法,合并起来?

abstract class SubstD implements PieVisitorI {
    Object n;
    Object o;
    SubstD(Object _n, Object _o) {
        n = _n;
        o = _o;
    }
    public PieD forBot() {
        return new Bot();
    }
    public abstract PieD forTop(Object t, PieD r);
}

class SubstV extends SubstD {
    SbustV(Object _n, Object _o) {
        super(_n, _o);
    }
    public PieD forTop(Object t, PieD r) {
        if (o.equals(t))
            return new Top(n, r.accept(this));
        else
            return new Top(t, r.accept(this));
    }
}

class LtdSubstV extends SubstD {
    int c;
    LtdSubstV(int _c, Object _n, Object _o) {
        super(_n, _o);
        c = _c;
    }
    public PieD forTop(Object t, PieD r) {
        if (c == 0)
            return new Top(t, r);
        else
            if (o.equals(t))
                return new Top(n, r.accept(LtdSubstV(c-1, n, o)));
            else
                return new Top(t, r.accept(this));
    }
}

小技巧

第八条建议

When extending a class, use overriding

to enrich its functionality.

根据以上建议, LtdSubstV 可以直接在 SubstV 类上进行继承和扩展。

class SbustV implements PieVisitorI {
    Object n;
    Object o;
    SubstV(Object _n, Object _o) {
        n = _n;
        o = _o;
    }
    public PieD forBot() {
        return new Bot();
    }
    public PieD fotTop(Object t, PieD r) {
        if (o.equals(t))
            return new Top(n, r.accept(this));
        else
            return new Top(t, r.accept(this));
    }
}

class LtdSubstV extends SubstV {
    int c;
    Object n;
    Object o;
    LtdSubstV(int _c, Object _n, Object _o) {
        super(_n, _o);
        c = _c;
    }
    public PieD forTop(Object t, PieD r) {
        if (c == 0)
            return new Top(t, r);
        else
            if (o.equals(t))
                return new Top(n, r.accept(LtdSubstV(c-1, n, o)));
            else
                return new Top(t, r.accept(this));
    }
}

小技巧

到此章结束,自定义类型及其相关运算操作都完全的面向对象化了。

Be a Good Visitor

tags:java, oop
category:java

先引入 super 关键字。

class ShadowedCartesianPt extend CartesianPt {
    int tx;
    int ty;
    ShadowedCartesianPt(int _x, int _y, int _tx, int _ty) {
        super(_x, _y);
        tx = _tx;
        ty = _ty;
    }
    int distanceTo0() {
        return super.distanceTo0()
               +
               (int)Math.sqrt(tx * tx + ty * ty);
    }
}
interface ShapeVisitorI {
    boolean forCircle(int r);
    boolean forSquare(int s);
    boolean forTrans(PointD q, ShapeD s);
}

abstract class ShapeD {
    abstract boolean accept(ShapeVisitorI ask);
}

class Circle extend ShapeD { // 圆心在坐标原点的圆
    int r;
    Circle(int _r) {
        r = _r;
    }
    boolean accept(ShapeVisitorI ask) {
        return ask.forCircle(r);
    }
}

class Square extend ShapeD { //左上角在坐标原点的正方形
    int r;
    Square(int _r) {
        r = _r;
    }
    boolean accept(ShapeVisitorI ask) {
        return ask.forSquare(r);
    }
}

class Trans extend ShapeD { //在指定位置的图形,
    PointD q;
    ShapeD s;
    Trans(PointD _q, ShapeD _s) {
        q = _q;
        s = _s;
    }
    boolean accept(ShapeVisitorI ask) {
        return ask.forTrans(q, s);
    }
}
// 检查某个点是否在图形内部
class HasPtV implements ShapeVisitorI {
    PointD p;
    HasPtV(PointD _p) {
        p = _p;
    }
    public boolean forCircle(int r) {
        return p.distanceTo0() <= r;
    }
    public boolean forSquare(int s) {
        return p.x <= s && p.y <= s;
    }
    public boolean forTrans(PointD q, ShapeD s) {
        return s.accept(new HasPtV(p.minus(q)));
    }
}

书中说下面精彩的地方到了。

主要就是揭示了 interface 也能够被继承扩展,

和访问者模式的精华所在:兼有灵活性及扩展性。

class Union extends ShapeD {
    ShapeD s;
    ShapeD t;
    Union(ShapeD _s, ShapeD _t) {
        s = _s;
        t = _t;
    }
    boolean accept(ShapeVisitorI ask) {
        ((UnionVisitorI)ask).forUnion(s, t);
    }
}

interface UnionVisitorI extends ShapeVisitorI {
    boolean forUnion(ShapeD s, ShapeD t);
}

class UnionHasPtV extends HasPtV implements ShapeVisitorI {
    UnionHasPtV(PointD _p) {
        super(_p);
    }
    public boolean forUnion(ShapeD s, ShapeD t) {
        return s.accept(this) || t.accept(this);
    }
}
class HasPtV implements ShapeVisitorI {
    PointD p;
    HasPtV(PointD _p) {
        p = _p;
    }
    ShapeVisitorI newHasPt(PointD p) {
        return new HasPtV(p);
    }
    public boolean forCircle(int r) {
        return p.distanceTo0() <= r;
    }
    public boolean forSquare(int s) {
        return p.x <= s && p.y <= s;
    }
    public boolean forTrans(PointD q, ShapeD s) {
        return s.accept(newHasPtV(p.minus(q)));
    }
}

小技巧

第九条建议

If a datatype may have to be extended,

be forward looking and use a

constructor-like(override) method

so that visitors can be extended too.

正是由于上面的提取出了 newHasPt 方法,下面直接重载即可。

class UnionHasPtV extends HasPtV implements UnionVisitorI {
    UnionHasPtV(PointD _p) {
        super(_p);
    }
    ShapeVisitorI newHasPt(PointD p) {
        return new UnionHasPtV(p);
    }
    public boolean forUnion(ShapeD s, ShapeD t) {
        return s.accept(this) || t.accept(this);
    }
}

The State of Things to Come

tags:java, oop
category:java

之前的章节每次都是生成新的实例,然后再对该实例进行操作。

这一章节主要讲到如何修改实例中的属性,然后再进行操作。

interface PiemanI {
    int addTop(Object t);
    int remTop(Object t);
    int substTop(Object n, Object o);
    int occTop(Object o);
}

class PiemanM implements PiemanI {
    PieD p = new Bot();
    public int addTop(Object t) {
        p = new Top(t, p);
        return occTop(t);
    }
    public int remTop(Object t) {
        p = (PieD)p.accept(new RemV(t));
        return occTop(t);
    }
    public int substTop(Object n, Object o) {
        p = (PieD)p.accept(new SubstV(n o));
        return occTop(n);
    }
    public int occTop(Object o) {
        return ((Integer)p.accept(new OccursV(o))).intValue();
    }
}

interface PieVisitorI {
    Object forBot();
    Object forTop(Object t, PieD r);
}

abstract class PieD {
    abstract Object accept(PieVisitorI ask);
}

class Bot extends PieD {
    Object accept(PieVisitorI ask) {
        return ask.forBot();
    }
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, Object _r) {
        t = _t;
        r = _r;
    }
    Object accept(PieVisitorI ask) {
        return ask.forTop(t, r);
    }
}

class OccursV implements PieVisitorI {
    Object a;
    OccursV(Object _a) {
        a = _a;
    }
    public Object forBot() {
        return new Integer(0);
    }
    public Object forTop(Object t, PieD r) {
        if (t.equals(a))
            return new Integer(((Integer)(r.accept(this))).intValue()
                               +
                               1);
        else
            return r.accept(this);
    }
}

class SubstV implements PieVisitorI {
    Object n;
    Object o;
    SubstV(Object _n, Object _o) {
        n = _n;
        o = _o;
    }
    public Object forBot() {
        return new Bot();
    }
    public Object forTop(Object t, PieD r) {
        if (o.equals(t))
            return new Top(n, (PieD)r.accept(this));
        else
            return new Top(t, (PieD)r.accept(this));
    }
}

class RemV implements PieVisitorI {
    Object o;
    RemV(Object _o) {
        o = _o;
    }
    public Object forBot() {
        return new Bot();
    }
    public Object forTop(Object t, PieD r) {
        if (o.equals(t))
            return r.accept(this);
        else
            return new Top(t, (PieD)r.accept(this));
    }
}

修改 PieVisitorI 中的方法签名,传入本身。

这样两者就完全相通,原对象可以请求访问者中对应的方法,

访问者可以在其内部的方法中修改原对象。

小技巧

这一点也可以从 this that 来理解。

interface PieVisitorI {
    Object forBot(Bot that);
    Object forTop(Top that);
}

同步修改其它相关代码。

abstract class PieD {
    abstract Object accept(PieVisitorI ask);
}

class Bot extends PieD {
    Object accept(PieVisitorI ask) {
        return ask.forBot(this);
    }
}

class Top extends PieD {
    Object t;
    PieD r;
    Top(Object _t, Object _r) {
        t = _t;
        r = _r;
    }
    Object accept(PieVisitorI ask) {
        return ask.forTop(this);
    }
}

class OccursV implements PieVisitorI {
    Object a;
    OccursV(Object _a) {
        a = _a;
    }
    public Object forBot(Bot that) {
        return new Integer(0);
    }
    public Object forTop(Top that) {
        if (that.t.equals(a))
            return new Integer(((Integer)(that.r.accept(this))).intValue()
                               +
                               1);
        else
            return that.r.accept(this);
    }
}

class SubstV implements PieVisitorI {
    Object n;
    Object o;
    SubstV(Object _n, Object _o) {
        n = _n;
        o = _o;
    }
    public Object forBot(Bot that) {
        return new Bot();
    }
    public Object forTop(Top that) {
        if (o.equals(that.t))
            return new Top(n, (PieD)(that.r).accept(this));
        else
            return new Top(that.t, (PieD)(that.r).accept(this));
    }
}

class RemV implements PieVisitorI {
    Object o;
    RemV(Object _o) {
        o = _o;
    }
    public Object forBot(Bot that) {
        return new Bot();
    }
    public Object forTop(Top that) {
        if (o.equals(that.t))
            return that.r.accept(this);
        else
            return new Top(that.t, (PieD)(that.r).accept(this));
    }
}

上面的代码还是有新的对象实例生成,

接下来,咱们通过彻底地修改实例的属性,而不是重新生成新的实例,来实现一个访问者。

class SubstV implements PieVisitorI {
    Object n;
    Object o;
    SubstV(Object _n, Object _o) {
        n = _n;
        o = _o;
    }
    public Object forBot(Bot that) {
        return that;
    }
    public Object forTop(Top that) {
        if (o.equals(that.t))
            that.t = n;
            that.r.accept(this);
            return that;
        else
            that.r.accept(this);
            return that;
    }
}

小技巧

第十条建议

When modifcations to objects are

needed, use a class to insulate the

operations that modify objects.

Otherwise, beware the consequences of

your actions.

咱们再一个例子,加深一下理解。

abstract class PointD{
    int x;
    int y;
    PointD(int _x, int _y){
        x = _x;
        y = _y;
    }
    boolean closerTo0(PointD p) {
        return distanceTo0() <= p.distanceTo0();
    }
    PointD minus(PointD p) {
        return CartesianPt(x - p.x, y - p.y);
    }
    int moveBy(int tx, int ty) {
        x = x + tx;
        y = y + ty;
        return distanceTo0();
    }
    abstract int distanceTo0();
}

class CartesianPt extends PointD{ //笛卡尔坐标
    CartesianPt(int _x, int _y){
        super(_x, _y);
    }
    int distanceTo0(){
        return (int)Math.sqrt(x * x + y * y);
    }
}

class ManhattanPt extends PointD{ //曼哈顿坐标
    ManhattanPt(int _x, int _y){
        super(_x, _y);
    }
    int distanceTo0(){
        return x + y;
    }
}

class ShadowedManhattanPt extends ManhattanPt{ //曼哈顿坐标
    int tx;
    int ty;
    ManhattanPt(int _x, int _y, int _tx, int _ty){
        super(_x, _y);
        tx = _tx;
        ty = _ty;
    }
    int distanceTo0(){
        return super.distanceTo0() + tx + ty;
    }
}

关于我

  • 标签: Linux、Emacs、Python、Scheme、Bash Shell
  • 业余: 佛学、toastmaster俱乐部演讲
  • 运动: 桌上足球、羽毛球、台球、乒乓球
  • 性格: 内向、中庸
  • 爱好: 看书、编程、折腾跟计算机相关的事
  • 联系: chenjiee815@gmail.com

我想说的话

我看这本书的缘起是王垠前辈 解密“设计模式” 一文对本书的推荐。

小技巧

该篇博文已经被王垠删除,所以就没有链接。

不过网络上转载的很多,搜索一下就可以了。

Indices and tables