五步阅读法

简要总结一下自己最近的阅读方法,还需要不断迭代更新。

阅读的基本目标应该是知行合一,所以以娱乐为目的的阅读不在本文讨论范畴。本文适用于读书,也适用于阅读中短篇的文章。

第一步 速览

这一步是专门针对我这样的急性子而设定的,拿到一本好书,总是会火急火燎的想把它看完,虽然仔细想想,怎么才算把一本书看完,也是很需要思考的重大问题。所以,如果实在克制不了,也不妨放自己一马,先快速读一遍,也算是留个大概印象。另外一种情况是快速读一遍之后发现这本书不值得细读,也算是节约自己时间了。从这个角度出发的话,速读也大可不必,速览一遍,观其大概就行了,当是挑书的一个过程。

第二步 演读

不知道这个词是不是我生造的,我的意思是详细的去读书,详细到作者的思路能在脑子里演绎,如果不能达到这个程度,可能有几个原因:

  1. 你的理解能力不够,背景知识不够,这本书还没到你读的时候;
  2. 你需要多读几遍;
  3. 这是本烂书。

如何分辨这三种情况,实在是一个困难的事情,需要不断的积累经验,一般而言一个领域懂得越多,分辨起来就会越轻松。

演读的目的在于真正把书的内容吃透,理解作者的思想。

注意这个过程最好不要做笔记,更不要画重点,不要用手头的忙碌掩盖思维的懒惰,做出一副认真读书的样子自欺欺人。

第三步 提取

把书合上,开始做笔记,根据书的信息容量,看完一部分还是全部看完再做这一步可以灵活把握。

合上书做笔记,是一种简单而有效的自我检查手段,它要求你把学到的东西再反馈到纸上,这个笔记可以是提纲挈领式的,只要能根据这些提要把所记忆理解的东西复述出来,当然有时间的话还是越详细越好。实在没有时间或条件,至少合上书,自问自答一番。

做完笔记,可以再翻开书比对一番,看是否有错误与遗漏。

第四步 连网

要想链牢固的掌握所阅的东西,关键是把它转化为自己知识体系的一部分,将它链接到已有的神经网络中去。有如下基本基本的方法。

  1. 比较:跟已经掌握的知识做比较,看看有哪些异同点,是不是已有知识的另外一个面目,或者一个重要的补充;
  2. 反思:书中所说的有没有不准确的地方,有没有不详细的地方,或者能不能换一个更易于理解的说法。

第五步 践行

这是最后一部,也是最重要的不能省略的一步,尤其是对于有方法论指导的书而言,如果一本书中有一个重要的实践手段得到了你的理解认同,那么最好就是照着去做,这是阅读的最佳归宿,一本书能有这么一个点就是价值百倍了。

当然,不是所有书都有直接的方法论可供实践的,那么可以有几个退而求其次的办法:

  1. 读书笔记,这个读书笔记最好是对第三步笔记的改进版,用你自己的理解去重新整理,或者添加了你自己的理解,或者用学到的道理去阐释一个新的问题。
  2. 交谈,向一个没看过这本书的人去介绍其中的内容,根据不同的交流对象,选择合适的方式和内容,但是传递同样的思想内核。

到这里,大概算是读完了一本书。

最后要说明的是这五个步骤不是严格割裂和先后排序的,具体应用时可以灵活裁剪及交叉进行,但是最后一步是不能省略的,否则就只能算是娱乐性阅读,即使你读的是相当严肃的书。

高可配置性软件开发

可配置性是软件工程实践中的重要问题之一,配置文件与可执行文件一起构成了软件运行时版本控制的要素,Visual Basic,Python、Lua等解释型语言,常以所谓“脚本”的形式出现,进一步提升了操作系统、游戏等大型软件的可配置性,而领域驱动设计在强调领域模型的同时,也带动了描述配置领域模型的领域特定语言的发展。在工业控制领域,软件的可配置性用组态的概念来描述;在游戏开发领域,一般用引擎和脚本的概念来描述。

1 基本原理

软件复杂性的根源在现实世界的复杂性,软件的复杂性超过人的理解能力时,会带来软件开发的困难乃至失败,好的软件工程方法论能给出现实世界的良好抽象模型,通过合理的抽象层次结构管理软件的复杂性,使人能从整体到细节分层次的把握软件。源自现实世界的复杂性并不会降低或消失,但却得到了约束。

高可配置性可在一定程度上解决快速需求变更问题,把握软件中不变的部分作为模型,将易变的部分通过配置文件等方式约束起来。

highly-configurable-1

2 构建方法

构建高可配置性软件,首先应当针对领域特点建立通用软件模型,然后针对模型使用配置技术。不同的配置加上模型最后构建了不同的软件实例。

highly-configurable-2

2.1 建模

建模问题是一个很复杂的问题,取决于设计时不同的场景,需要领域知识与设计经验,并且和领域专家密切合作。这方面有几本经典的书:《领域驱动设计》《实现领域驱动设计》

2.2 配置技术

2.2.1 配置文件

配置文件是使用最广泛也是最基本的一种配置技术,配置文件主要用于描述软件启动及运行时所需要的数据,一般不直接描述软件的运行逻辑,常见的配置文件格式包括ini、xml、json等,简单比较如下表所示:

格式 表达能力 可读性 典型应用场景 举例
ini 较弱,不可嵌套 最好 软件启动参数。 启动目录、ip地址。
xml 最强 最差 复杂对象的序列化。 数据帧结构与解析规则。
json 一般 一般 web API接口。 天气预报服务的接口。

2.2.2 脚本语言

一些大型的程序,往往通过结合多种编程语言来实现,一般通过编译型语言实现系统的相对固定的部分与运行效率关键部分,通过解释型语言来实现系统的易变部分。比如Linux操作系统用C实现了很多工具,而通过bash脚本对这些工具进行综合应用,大型游戏基于C++游戏引擎构造,而使用Lua脚本等描述游戏动作逻辑。

2.2.3 领域特定语言

领域特定语言介于一般的配置文件和程序代码之间,即针对某一特定领域,具有受限表达性的一种计算机程序设计语言。

领域特定语言又分为内部领域特定语言、外部领域特定语言、语言工作台等,其中内部领域特定语言是主要语言程序代码的一种特殊写法,不能算是配置,外部领域特定语言则设计新的语言来进行表达,仍可归为文本配置的范围。而语言工作台则主要提供可视化的配置方式或者成为可视化编程。

领域特定语言介于配置文件和脚本程序之间,提供了领域内较高的表达能力,也便于领域专家的理解,可加速专家知识向软件的生成。

针对不同模型的需要,以上3种配置技术在高可配置性软件开发均可应用,下表对三种配置技术的主要特点和典型应用场景做了比较。

配置技术 表达能力 可读性 典型应用场景 举例
配置文件 描述软件运行需要的数据,一般不直接描述软件运行逻辑。 取决于具体的配置文件格式。 软件启动参数。 启动目录、网络通信地址。
脚本语言 直接描述软件的运行逻辑。 仅适合程序员理解,不利于领域专家理解。 基本接口形式固定时,描述易变逻辑。 在C++构建的设备监控接口基础上用Python脚本描述监控流程。
领域特定语言 描述领域模型的配置,从而间接描述软件运行逻辑。 既适合程序员理解,也适合领域专家理解。 建立了认可度较高的领域模型,模型的配置丰富易变。 用“状态-反应”模型表现故障处理,建立领域特定语言描述具体的内容。

3 工程实践

如图所示,高可配置性软件开发实际改变了传统软件的开发流程,突出了软件配置在软件生命周期中的重要地位。下面将阐述高可配置性对传统软件开发各阶段带来的影响。 highly-configurable-3

3.1 需求分析

在需求分析阶段,相比普通开发,高可配置性软件开发需要再重点分析以下问题:

  1. 采用高可配置性技术的必要性,这主要是由软件的适用范围决定的,即软件是否要具备可推广性,软件应对的需求是否易变。软件需要适配的场景越多,需求越易变,则必要性越大,如果软件只用于一个基本固定的场景,则没有必要使用配置。
  2. 区分模型需求与配置需求,在需求采集时,要采集软件在各种应用场景的需求,分析软件可能因推广带来的需求扩展,尽可能分析较多的案例,深入分析提取系统的不变性,抽象出系统的模型,而将系统的易变部分识别为配置需求。

3.2 软件构建

高可配置性软件开发的软件构建分为两个阶段:

  1. 软件主体构建:该阶段由程序员完成,高可配置性对架构设计要求较高,在使用面向对象范式时,需要深刻理解与应用面向对象的设计原则,大量应用各类构件型设计模式,以及反射、依赖注入等运行时关联技术;
  2. 软件配置阶段,该阶段可由程序员完成,也可由领域专家完成。由于配置成为软件的重要组成部分,所以对配置文件应当和对源代码一样严格管理,纳入版本管理系统,由于人工配置容易产生错误,所以应当尽量提供可视化的配置工具,限制用户随意修改配置。

3.3 软件测试

对应软件的两阶段开发,高可配置性软件的测试也分为两个部分:

  1. 软件主体测试,构造测试用例应当设计各种场景的配置,这些场景要考虑模型的边界情况,通过测试尽量消除软件主体的缺陷,减少软件推广应用时在配置后发现缺陷。

  2. 软件配置测试,完成配置测试才能在具体的场景应用,配置过程往往由领域专家而非程序员完成,因此最好对配置进行校验,并提供配置完成后的测试工具与测试方法细则,使得领域专家也能完成软件的测试。

3.4 软件维护

对应软件的两阶段开发,高可配置性软件的维护也分为两个部分:

  1. 软件主体维护,通用的功能升级通过对软件主体的升级完成,软件主体功能的修改应当保持配置的兼容,如果不得不改变配置的格式时,应当提供配置的升级转换功能;

  2. 软件配置维护,用户需求与应用场景发生了变化,而这些变化在软件主体预期之内时,可由领域专家修改配置完成,软件配置逐渐成为用户的重要资产。

如何创建一个对象(C#篇)

在面向对象编程范式的语言中,对象是最重要的一个概念,对象维护自身的状态,通过发消息(调用)来影响其他对象,从而完成整个程序的运转。何时以何种方式创建对象,是面向对象设计中需要重点考量的问题之一。本文对C#语言中创建对象的方法进行简要总结,涉及语言特性、设计模式等,所有的例子以能说明清楚问题为目的,不注重其实用性。相信本文对Java等其他面向对象语言也有相似的价值。

1. new与构造函数

一个C#对象封装了一些字段和一些方法(属性、事件等本质上都是方法),考察一个C#对象的内存图景可以发现,每个具体的对象实际上拥有的是字段,而方法本质上都是全局性的,对象上方法的调用A.b(...)可视为b(A,...),对象的this指针将作为方法的一个参数传入,从这个角度理解就不难发现,创建一个对象本质上需要根据对象的字段划分一块内存,并将这些字段的内容初始化。完成这项工作最直接的途径就是new与构造函数,这也是C#语言提供的构建对象的基本机制。

考虑下面这个简单的例子,Shape类主要抽象了计算面积这一行为,派生类RectangleCircle具体完成这一行为。

public abstract class Shape
{
    public abstract double Area();
}

public class Rectangle:Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
    public Rectangle(double width, double height)
    {
        Width = width;
        Height = height;
    }
    public override double Area()
    {
        return Width * Height;
    }
}

public class Circle : Shape
{
    public double Radius { get; set; }
    public Circle(double radius)
    {
        Radius = radius;
    }
    public override double Area()
    {
        return System.Math.PI*Radius*Radius;
    }
}

public class Test
{
    public static void Main()
    {
        Shape rectangle = new Rectangle(3, 4);
        Shape circle = new Circle(5);
        System.Console.WriteLine(rectangle.Area());
        System.Console.WriteLine(circle.Area());
    }
}

我们可以通过如下命令编译与查看IL代码

csc Shape.cs
ildasm Shape.exe

Test类中Main函数的IL代码如下:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // 代码大小       65 (0x41)
  .maxstack  2
  .locals init (class Shape V_0,
           class Shape V_1)
  IL_0000:  nop
  IL_0001:  ldc.r8     3.
  IL_000a:  ldc.r8     4.
  IL_0013:  newobj     instance void Rectangle::.ctor(float64,
                                                      float64)
  IL_0018:  stloc.0
  IL_0019:  ldc.r8     5.
  IL_0022:  newobj     instance void Circle::.ctor(float64)
  IL_0027:  stloc.1
  IL_0028:  ldloc.0
  IL_0029:  callvirt   instance float64 Shape::Area()
  IL_002e:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_0033:  nop
  IL_0034:  ldloc.1
  IL_0035:  callvirt   instance float64 Shape::Area()
  IL_003a:  call       void [mscorlib]System.Console::WriteLine(float64)
  IL_003f:  nop
  IL_0040:  ret
} // end of method Test::Main

在IL代码中,构造函数编译为一个名为.ctor的方法,而new操作有对应newobj操作符,生成一个对象实例后将其加入Main函数的栈,而调用对象方法时的操作callvirt则保证了在运行时根据继承树找到合适的方法。

很容易发现,普通方法Area是运行时确定的,但是构造函数却不得不在编译时确定,或者说,对象的使用是多态的,对象的创建则是具体的,因此,对象的创建,或者说new与构造函数这个机制,成为制约程序变化的瓶颈。

2 new的隔离与封装

基于1中的例子,考虑如下的需求,用户从命令行输入参数信息,第一个参数代表形状的种类,后续参数代表构造这个形状需要的数据,然后由程序计算面积。代码如下:

public class Test
{
    public static void Main(string[] args)
    {
        Shape shape = ShapeFactory.CreateShape(args);
        if(args[0]=="Circle")
        {
          shape = new Circle(double.Parse(args[1]));
        }
        else if(args[0]=="Rectangle")
        {
          shape = new Rectangle(double.Parse(args[1]),double.Parse(args[2]));
        }
        if(shape!=null)
        {
          System.Console.WriteLine(shape.Area());
        }
    }
}

考虑以下两点原因,我们需要进一步封装上述代码中的条件分支语句:

  1. 在应用Shape这个体系的时候,类似的根据不同的字符串来创建不同的子类的代码还可能很多,比如根据配置文件来创建Shape,根据数据库中的数据来创建Shape等,需要消除重复代码;
  2. Shape的种类可能不断增多,需要隔离变化。

封装出的方法CreateShape可以作为Shape类的静态方法,或者作为一个新的工厂类ShapeFactory的静态方法。

public class ShapeFactory
{
  public static Shape CreateShape(string[] args)
  {
    Shape shape = null;
    if(args[0]=="Circle")
    {
      shape = new Circle(double.Parse(args[1]));
    }
    else if(args[0]=="Rectangle")
    {
      shape = new Rectangle(double.Parse(args[1]),double.Parse(args[2]));
    }
    return shape;
  }
}

public class Test
{
    public static void Main(string[] args)
    {
        Shape shape = ShapeFactory.CreateShape(args);
        if(shape!=null)
        {
          System.Console.WriteLine(shape.Area());
        }
    }
}

这一般称为简单工厂,算是一种准设计模式,代码的扩展点位于CreateShape中的条件分支语句。

3 工厂方法和抽象工厂

前述2中的ShapeFactory是一个具体类,如果提取出一个接口IShapeFactory来,二者就构成了一个工厂方法模式,不过这么做没什么意义,工厂方法更多的是体现在平行的类层次上,考虑这样的需求,假设我们在一个编辑环境中编辑这些Shape,双击它们的会弹出不同的设置窗口,即对不同的Shape有不同的Editor,这时候可以在抽象类Shape中加入一个工厂方法CreateEditor,它的具体创建行为延迟到子类中决定。

public abstract class Shape
{
    ...
  	public abstract Editor CreateEditor();
}

public class Rectangle:Shape
{
    public override Editor CreateEditor()
      {
        return new RectangleEditor(this);
      }
}

public class Circle : Shape
{
    public override Editor CreateEditor()
      {
        return new CircleEditor(this);
      }
}

public abstract class Editor{}
public class RectangleEditor : Editor{}
public class CircleEditor : Editor{}

具体的创建行为在子类中,但是它们的关系依然是编译时决定并绑定死的。

抽象工厂可以视为工厂方法的扩展,假如Shape除了构造Editor类体系外,还构建自己的3D形式,那么需要在Shape中再加入一个Create3D方法,返回ThreeDShape,这时Shape类构建了一系列相关的类,成为一个抽象工厂,当然这时Shape类太过复杂,可以考虑把这些创建函数都提取到ShapeFactory中去,这里就不再赘述了。

4 反射工厂

回到1中的ShapeFactory,它的扩展点主要是根据不同的字符串生成不同的类,这是创建对象时最常见的场景之一,这个字符串往往来自于用户配置,如果事先知道有多少种子类,那么这个条件分支已经是程序中唯一的扩展点了,也是可以接受的,但如果事先不知道有多少种子类,比如我们把Shape类作为一个库提供给程序员使用,那么ShapeFactory的位置就会比较尴尬,而.Net的反射机制则能一劳永逸的解除这个尴尬。

根据一个字符串,创建一个同名的对象是很自然的想法,但在C++中,很难实现,因为C++的类编译后不再保有自己的类名信息,而.Net则在运行时一直保有类的详细信息。使用反射改写工厂方法如下:

public class ShapeFactory
{
  public static Shape CreateShape(string[] args)
  {
    System.Type type = System.Type.GetType(args[0]);
    object[] shapeArgs = new object[args.Length - 1];
    for(int i = 0; i < args.Length - 1; i++)
    {
      shapeArgs[i] = double.Parse(args[i+1]);
    }
    return (Shape)System.Activator.CreateInstance(type, shapeArgs);
  }
}

反射是C#和Java这类静态语言获得一定动态性的重要机制,在一些动态语言如Python中,由于Eval机制的存在,字符串和代码基本上是不分家的。

5 序列化与反序列化

简单理解的话,序列化等同于把运行时对象存储为一个字符串,而反序列化则等同于从这个字符串(常使用xml格式)恢复对象。一般可以用工厂方法来实现。比如可以这样设计Shape的反序列化。

public abstract class Shape
{
    ...
  	public static Shape Load(string str)
    {
      ...
    }
  	public abstract string Save();
}

Load方法中,由于要根据字符串来辨别子类,所以离不开反射机制。

.Net提供了比较友好的序列化与反序列化机制,只要我们设计类型时遵循一定的原则,比如需要无参构造函数等,就可以直接使用该机制。比如在Rectangle中加入无参构造函数后,可以把它保存到xml文件再恢复出来。

Shape rectangle = new Rectangle(3, 4);
XmlSerializer mySerializer = new XmlSerializer(typeof(Rectangle));
StreamWriter myWriter = new StreamWriter("rectangle.xml");
mySerializer.Serialize(myWriter, rectangle);
myWriter.Close();
FileStream myFileStream = new FileStream("rectangle.xml", FileMode.Open);
Shape rectangle2 = (Rectangle)mySerializer.Deserialize(myFileStream);

由于序列化和与反序列化机制的成熟,用类似上面的代码可以轻松的实现对象克隆,所以原型模式在C#中比较少使用。

一般的Xml序列化有一个问题,它不能保持对象间的引用关系,微软还将WPF中的Xaml序列化单独拿出来做成了库,所以不想自己实现一个保持引用关系的序列化机制的话,可以考虑System.Xaml 命名空间。

6 依赖注入

关于依赖注入的原理,看懂老马的这篇文章就够了。

依赖注入要解决的是一系列相关对象的创建问题,对象A的组成部分之一B是一个抽象类,那么在创建A的时候就免不了要创建B的实例,而这就免不了具体类的出现,因此需要使用工厂类完成这项工作,这样类A才能真正和B的具体子类解耦。依赖注入通过一个公有的的工厂机制来解决这个问题,只要你的类按照一定的规则设计,比如提供构造函数或者提供属性,用于设定类中包含的抽象类型的具体实例。

依赖注入框架一般支持代码创建对象,也支持xml配置文件创建对象,第一种方式将对象创建代码集中起来,但对象类型仍然是编译时确定的,而第二种方式则借助反射,使用描述式编程来管理对象创建,完全实现运行时确定对象类型。

C#中的依赖注入框架,除了官方的Unity外,还有Autofac,Ninject等。

对象的创建问题,到依赖注入框架,基本上也就到极致了。

写作与跑步

前几天报名了马拉松,准备跑个半马,不辜负这美好的空气。昨天晚上做了第一次热身,跑了十二公里,跑下来比较轻松,只是在大约八公里的地方腿酸得不行,一副不停下来就要断掉的架势,好在我还了解一点运动理论,明白这个时候大脑是在提前预警而已,实际上人的潜能远不止如此,所以感受着酸爽,坚持了一会儿,那种感觉也就过去了。

今年我发了宏愿,要每天更新一篇博客,写了十几天之后,就感觉快要江郎才尽,无话可说了,这就像一个没怎么跑过步的人,上来跑了一公里就气喘吁吁一样。长跑的人都知道,在超越体能极限的时候是不能停下来的,即使步频和步幅会有所下降,也要保持动作的规范,不能从跑变成走,否则就难以超越极限,体会到长跑特有的那种乐趣。

昨天我跑到第八公里的时候,还想到了我的博客,乃至我的生活,在极限的压力逼迫时,你的思考可能不再锐利,但是唯一不能忘记的是迈动你的双腿,,这个时候技巧不是最重要的,最重要的是坚持。所以我的的博客每天更新一篇要怎么坚持下去呢?当然需要以输出带动输入,学习整理很多东西,这是我要做这件事的初衷。可是最简单也最重要的一点是什么呢?是无论发生任何情况,每天一篇坚持写下去,即使质量不高,即使绞尽脑汁。

这几年我研究了不少方法论的东西,发现可能最重要的方法论之一是形式主义的坚持实施,就是把一种形式落到实处,坚持不懈,当然形式对实际的促进作用程度不一,但大多数人的问题不是形式不好,而是根本没有坚持,正如跑步姿势再标准,坚持不下去的化,也无法完成马拉松一样,看过了无数的学习英语的技巧,但却学不好英语。太多的小聪明之外,忘记的是笨办法的伟大之处。

在极限中迈开双腿,这是长跑的秘诀,是长跑带给人生的启示,也正是长跑的魅力所在吧。

干货的问题在哪里

在我们湖南老家,夏天的时候,一场雨水一天阳光,地里的蔬菜就会噌噌噌的长,多到吃不完,那个时候,我妈就会把黄瓜、葫芦瓜、豆角等等简单处理一下后都拿去暴晒,做成菜干,冬天的时候,放到腊肉火锅中,某种程度上菜干是蔬菜味道的精华,所以吃起来相当美味。

不知道从什么时候开始,人们看书看文章也喜欢起干货来,经常看到有人会评价说,这本书不就是说了一个什么什么道理么?需要说这么多页么?我用100个字就概括了。当然不排除有一些书会为了多卖几块钱而注水,但是很多时候情况不是这样。

一本书往往只写了一个主题,或者讲了一个故事,比如《断舍离》想要教你如何反思消费主义,精简自己的物品,《西游记》讲的是唐僧师徒西天取经,结果他们去到了真经,都成佛了,这两本书的书名都很好,看书名就知道了书的主要意思,如果要看最干的干货,看书名是不是就足够了呢?阐释和详述的意义到底在哪里呢?

吃蔬菜时吃进去的水分究竟有什么意义呢?

为了迎合爱干货的人,现在像《罗辑思维》之类的还提供了微缩书的服务,把一本书的干货挑给你看,看这样的浓缩和看原书有什么区别呢?我觉得有两个区别。

一个主要的区别在于细节可以带来可信度,回归到我们看书的目的,对于非虚构类的书一般带有一定功利性,我们一定是想解决某些问题或者获得某些思想采取读书的,如果我们看的书没有值得我们去相信的东西,那显然没有看的必要,但是缺乏细节会使得我们只是在眼前晃了一些概念,而没有真正理解这些概念,把它们训练成大脑中的一种模式,因此去践行这些方法或思想的可能性就很小,看干货的结果就是看的时候觉得说得似乎都挺好,但也仅此而已了,看虚构类的书就更需要细节了,否则就完全沦为了装逼行为,而不是从故事中去寻找愉悦与精神享受。

另外一个区别在于细节带来的意外启发,一本好的书是作者的思想流露,尽管它有主要的话题,但作者往往也会偏题,这种偏题可能带给读者意外的收获,比如喜欢美食的人看《红楼梦》可能学会了做几道菜,看育儿的书提高了沟通能力,改善了自己的社交关系。

干货存在的意义除了给假装努力的人安慰外,还可以给真正努力的人一个目录,但也仅此而已。

真正好的东西值得反反复复从不同的角度去阐释,正如蔬菜还是带着水更原汁原味一样。浓缩的是精华,但也可能是一厢情愿和消化不良。

货币的现金价值

1 现值和终值

终值取决于现值、利率和复利次数,而且复利次数的作用比想象中还要重要,所以理财越早越好。

反过来计算,未来的钱是有一定画饼性质的,因为通过膨胀率一般都是正的,这时候称为贴现率,中国的通货膨胀率 近30年平均约为6%。

2 年金与定投

终值取决于每年支付额、利率、复利次数(年份)。

反过来可以根据终值、年份和利率计算每年支付额。

比如,30年后想要有1000万继续,年化利率按10%计算,那么每年要攒多少钱呢?

10000000*0.1/((1+0.1)**30-1)
60792

每年攒6万多就好了,坏消息是,30年后的1000万,按贴现率6%计算,只相当于

10000000/(1+0.06)**30
1741101

现值174万,所以说通胀猛于虎啊。

如果能早攒10年钱呢?还是每年攒6万多,年化利率10%,那么终值就会变成

60792*(1.1**40-1)/0.1
26906086

不止翻了一倍啊!

所以理财要趁早啊,不过现代人的寿命越来越长,理财的作用也会越来越明显。

3 分期付款

年金的现值

反过来计算分期付款额度(等额本息)

比如300万的房贷,年利率5%,还30年,则月供

(3000000*0.05/(1-1.05**(-30)))/12
16262

1.6万。

我读了几本书?

读书这回事,我常说,跟打麻将一样,算是个人爱好,并不见得高人一等。可是这种说辞有点自我催眠的意思,因为下意识里还是会把读书当成一个有些追求的活动,可能有两个原因,一是因敬畏知识而敬惜字纸,二是消解内心中渴望进步的焦虑。

最近几个月重看了好几本书,书上的字词自然是没有变化,但是对书的理解却好似之前没读过一般,因为这些重读的书中的很多道理或者做法,我已经有一些行动的经验了,此时带来的共鸣自然不可同日而语。

读虚构类的书是一种享受,一般而言还上升不到行动。而读非虚构类的书,则需要判断它说得有没有道理,方法可不可行,如果答案是肯定的,那么就得照着去做,否则读它作甚呢?读了书,觉得讲得好,而没有去做,只不过是一次智力上的自亵,然而并没有什么卵用,用在这里算是合情合理了。

很多年前,我看读者还是青年文摘的时候,看到一篇鸡汤文,说某个穷苦人,因为看了读者还是青年文摘中的鸡汤文,发奋图强,成了小有名气的破乱大王,我想,她真的是很牛,看了一篇文章就发财了,我看了那么多本书,什么时候能发财呢?我现在愿意相信这个鸡汤文是真的,好用的道理也许真的只要一个就够了,好用的道理往往是简单的道理,但是坚持践行却很难,比如巴菲特不会盯着股价炒股而是去研究企业来投资,这么简单的道理真正能做到的又有几人?中国古话讲知易行难,大致如此了。

我算的上是一个喜欢看书的人,但是又顶瞧不上眼高手低、百无一用的书生,书生的臭毛病在于认为读了书就高人一等,本质上跟背了个名牌包包就觉得自己高人一等的拜金女一样,我们湖南农村有句话叫“读书读到屁眼里去了”,我有时候也不禁想想,我把书读到哪里去了呢?

今天我在重读某本书的时候,发现书中的道理我非常认同,可是几年前读我也大概是认同的吧,但是这一次我有了强烈的要去行动的愿望,同时也不禁汗颜,因为我已经在这点差异之间,浪费了几年的时间。

所以,最后我能很好的回答标题的问题了,读了几本书根本不重要,重要的是践行了多少?正如下赌场打麻将,打了几盘不重要,重要的是赢了多少钱。我听过了无数遍知识就是力量这个道理,但直到今天,才明白从知识到力量的过程中,还有一剂催化剂,缺了它,读书就真的跟打麻将一样了。

朝闻道家庭基金成立及2016年年报

为了更好的整理投资活动,自本年度起,成立“朝闻道”家庭投资基金,统一按净值法计算收益,每月发布月报,2016年的投资活动已经很难一一追溯,所以简单的将2016年投入的资金都按年初投入计算,据此计算,2016年年末净值为1.2526。

时间 净值
20160101 1.0000
20161231 1.2526

期末持仓品种:

  1. A股市场:贵州茅台、双汇发展、伊利股份、恒瑞医药、格力电器、美的集团、老板电器、丽江旅游、中青旅、张家界、宇通客车、福耀玻璃、万科A、雅戈尔、平安银行、科大讯飞、鼎泰新材。
  2. 基金:广发中证养老产业指数A、富国中证红利指数增强、博时安怡6个月定开债券
  3. 其他:拍拍贷、比特币。

2016年度总结

2015年开始接触投资,主要品种包括余额宝、指数基金、P2P平台等,其中指数基金投入较少,但借着2015年股市的大牛市赚了一些,而且在市场情绪狂热的时候(食堂里人人都在讨论,沪市指数5000点左右)成功逃跑,P2P投入十多个平台,收益从8%~18%不等。

2016年才是真正开始投资的一年,30出头才懂得滚雪球的基本道理,亡羊补牢,犹未晚也。2016年逐渐将投资从P2P平台转移至股市,年初股灾熔断的时候尚未建仓,熔断机制停止后续陆续买入H股ETF,贵州茅台,双汇发展等,后续投资策略主要为持有蓝筹股打新。今年股市收益约20%,由于资金是陆续投入的,实际净值应略高于1.2,总的来说今年的运气真的是相当好。

收益主要来自三个部分:

  1. 贵州茅台、福耀玻璃等蓝筹股的上涨;
  2. H股ETF和H股B的波段操作,英国退欧和川普当选两次事件都赚了钱;
  3. 由于资金总量小,中的一次新股也贡献了不少收益。

茅台买入的逻辑:

  1. 相比历史最高价不算贵;
  2. 随着消费升级,每个喝白酒的都会想买来尝尝;
  3. 不少小散户因为茅台的票面价格高而认为茅台贵(我也不敢相信这样也敢投资股票,但接触过后真的不少人这样想)。

H股ETF买入的逻辑:

  1. 历史估值相对低位,PE低于10;
  2. 港股受外盘影响大,但H股实际上大部分是大陆国企,市场反应有时候似乎过度了,可以波段操作。(但是仔细分析一下,波段操作有亏有赚,总的来说也赚了钱,但是如果一直拿着不动,收益也不会差,最主要浪费了不少时间。)

亏损主要有两次:

  1. 在没看懂的情况下溢价买了军工B,结果被套利党吃了,知道了自己有多韭菜,赶紧割肉离场,承认自己的失败,以后自己没看明白的东西碰都不要碰;
  2. 在调整配置深市打新市值的时候,买得过于随意,万科A赶上险资炒作,被彻底埋了,但是考虑到万科A已经跌出了价值,如果割肉换股可能面临二次亏损,反正要持股打新,万科A刨除炒作本质上还是一家好公司,所以就不纠结买入价准备继续拿5年以上了。

另外投资了一点点比特币,浮盈150%,电子货币需要持续关注,投入小量资本参与,可以控制风险。

P2P投资逐渐退出,收益已经大不如前,风险不小,牵扯精力太多。准备最后只保留拍拍贷少量资金做散标投资,理由:

  1. 资金去向透明、坏账透明;
  2. 可以极度分散,符合高利贷投资的规律,在极度分散的前提下标称收益率减去坏账率,则实际收益率是可预期的,目前约为16%。

2017年股市投资的主要策略依然是持有蓝筹股打新,除非打新政策发生大的变化,或者通过学习我掌握了更可靠,预期收益更高的投资方法。预期收益保底10%,争取15%。

今年学习或体会到的一些重要投资原则,这里列一些提纲,还需要继续深入思考。

  1. 持续的现金流投入很重要,仅靠股市投资收入生活很难;
  2. 买入逻辑越清晰,越容易赚钱,不赚钱的时候拿着也安心;反之随意买入的亏损概率较高。
  3. 分散、重复,结合概率分析,可得到较准确的预期收益;
  4. 操作越少越好,出错机会少,税费少,节省时间;
  5. 浮盈即是挣钱了,也是没挣钱,浮亏既是亏钱了,也是没亏钱,价值投资是一条节俭生活的不归路;
  6. 投资品类需要做期限匹配,股市流动性好,但匹配期最好高于5年。
  7. 买入价其实只有心理锚点的作用,是否卖出最好不要考虑买入价的问题,否则及其容易追涨杀跌,步步走错。
  8. 学习知识可以降低风险,带来好运,这是最重要的。

磨洋工简要分析

人们习惯于将消极怠工、只出工不出力的现象称为“磨洋工”。对公司而言,磨洋工自然不是好事情,对员工而言,磨洋工有时却是无可奈何的选择。

什么时候会出现磨洋工?当公司简单的以工作时间来统计工作量时,工作效率高的人自然就会磨洋工,否则做得越快,事情就越多,却又得不到相应的回报,所以老油条员工在领受任务时,都会有意延长完成任务的期限,降低上级的预期,从而以安全的平均速度完成任务,即不会因为落后被批评或开除,也不会给自己揽太多吃力不讨好的事情。

显然,以实际的工作量来衡量员工的贡献更合理,可是工作量的统计标准又是相当成问题的,如果它太过主观,就变成了领导的一言堂,员工会开展办公室政治来争夺利益,如果它十分客观,那么员工会试图去理解评价标准,然后把自己的所有行为都适配到这个标准上,而标准是否与公司的综合利益完全一致有时是存在问题的,这就出现了刷KPI,刷评分而实际上损害公司利益的事情。

所以好的公司还是要有价值观的感召力,如果仅仅是为了钱而工作,往往会损害公司的长远利益。但话又说回来,价值观感召力是建立在基本的按劳分配机制上的,只能是一个补充机制,如果只谈理想不谈钱,那就是耍流氓,脑子没缺根筋的人是不会用自己的情怀来为别人的奢华买单的。

对员工而言,磨洋工是无奈的反抗,它最大的危险是降低自己进步的速度,在消极中泯灭了人生的激情,万一你不得不磨洋工,那么最好是能做一件事,那就是为跳槽做准备。

每一块钱都是平等的

每一块钱的价值都是一样的,这是常识。但是人们会有很多心理认知颠覆这一常识。

首先,人们会根据钱来得是否容易来决定是否需要珍惜这笔钱,比如十多年前,我有一个长辈,喜欢小赌怡情一下,一般输了钱回家就默默的,要是赢了钱,手上必定拎着几斤猪肉,因为反正钱是赢来的,不吃白不吃嘛。所以你知道澳门开赌场有多挣钱了,输钱了的自不必说,赢钱了的也会挥霍一番。可能辛苦搬砖挣来100块的舍不得用,要是地上捡的,那就可以铺张浪费一番了。可是仔细想想,100块就是100块,无论怎么得来,从投资理财的角度来看,都是没有任何差别的。

反过来想,贷款买房时,争取零点几的利率折扣,就是很多万块的差价,如果这笔钱要靠在菜市场讨价还价来节省,可能一辈子都不够,但是却有人会在买菜时对几毛钱斤斤计较,买房时却放松了警惕,没意识到小数字背后的大数字。

金钱的这种平等在某种意义上是有些残酷的,作为一般等价物,金钱抹去了人在付出劳动时的差异,只留下一个赤裸裸的数字,甚至连善恶也是不分的。

我不想讨论伦理的话题,只是想说,从投资理财的角度来看,每一块钱都是平等的。无论钱从哪里来,它都要进入你的资金池,获得同等的地位,在消费的时候,应该从你的资金池的能力出发,决策消费的必要性和档次,而不是因为某笔钱来得容易就专款专用了。

不理解这一点的人去投资股票,会像赌博的人一样,账面上浮亏的时候默默不出声,万一要是账面上浮盈了,甚至股票还拿在手上就先预支一笔钱来庆祝了,忘记了投资的长期性,浮盈的有可能会再变成浮亏,只要一日在股市,就难以称得上真正盈利了,这或许也是股市看似简单实则很难的原因,在很多方面它都是反人性的。作为一个价值投资者,必然要克制自己过度的欲望,合理的支配自己的每一块钱,战战兢兢的和市场先生做朋友,没有所谓的明确的成功的界限。