李小龙教我写代码

3 阅读5分钟

前言:让代码像水一样流动

在编程的世界里,我们常常被各种技术框架、设计模式和最佳实践所包围,容易陷入固定的思维模式。但如果我们换个角度,用哲学的视角来看待编程,会发现一个全新的世界。本文将探讨如何像李小龙所说的那样,让代码"像水一样"流动,从而写出更有活力、更具适应性的软件。

代码的两种状态:石头与水

石头般的代码

传统的代码往往像石头一样:

  • 固定的形状和结构
  • 大量的if-else堆砌
  • 防御性编程,害怕出错
  • 边界清晰但僵硬的类结构
  • 状态存储而非传递

这种代码虽然结构清晰,但缺乏灵活性,难以适应变化。

水般的代码

而水般的代码则具有以下特性:

  • 随形就势,适应变化
  • 响应式流动
  • 容错即本性,不害怕错误
  • 边界消融,函数组合
  • 状态传递(流)而非存储

正如李小龙所说:"Empty your mind, be formless, shapeless, like water."(清空你的思想,无形式,无形状,像水一样。)

水的三德,代码的三境

1. 无常形:函数组合 > 类继承

水没有固定的形状,它会适应容器的形状。在代码中,这意味着我们应该优先使用函数组合,而不是深层的类继承树。

石头代码

class BaseProcessor
{
    public virtual void Process() { /* 基础处理 */ }
}

class AdvancedProcessor : BaseProcessor
{
    public override void Process() { /* 高级处理 */ }
}

class SuperAdvancedProcessor : AdvancedProcessor
{
    public override void Process() { /* 超级高级处理 */ }
}

水代码

Func<Data, Data> Validate = data => { /* 验证逻辑 */ return data; };
Func<Data, Data> Transform = data => { /* 转换逻辑 */ return data; };
Func<Data, Data> Save = data => { /* 保存逻辑 */ return data; };

var pipeline = Validate.Compose(Transform).Compose(Save);
var result = pipeline(data);

2. 就下流:错误向下游传递

水总是往低处流,不会在中途拦截自己。在代码中,这意味着错误应该向下游传递,而不是被到处的try-catch拦截。

石头代码

class DataProcessor
{
    public void Process()
    {
        try
        {
            Validate();
            Transform();
            Save();
        }
        catch (Exception ex)
        {
            Log.Error(ex);
            // 错误被吞噬
        }
    }
}

水代码

class DataProcessor
{
    public Result<Data> Process(Data data)
    {
        return Validate(data)
            .Bind(Transform)
            .Bind(Save);
    }
    
    private Result<Data> Validate(Data data) { /* 验证逻辑 */ }
    private Result<Data> Transform(Data data) { /* 转换逻辑 */ }
    private Result<Data> Save(Data data) { /* 保存逻辑 */ }
}

3. 穿石柔:持续小步重构

水虽然柔软,但能水滴石穿。在代码中,这意味着我们应该持续进行小步重构,而不是追求一次性的完美。

石头代码

  • 大爆炸式重写,项目死亡
  • 代码质量逐渐恶化,直到无法维护

水代码

  • 每次提交都进行小的改进
  • 持续集成和测试,确保代码质量
  • 定期清理技术债务

哲学-代码的即时转换器

当我们遇到编程问题时,可以尝试用以下方式提问,从石头思维转向水思维:

常规问法水的问法哲学来源
"怎么防止出错?""错误如何自然流走?"道家:无为
"这个类该怎么设计?""这股能量该向何处去?"赫拉克利特:流变
"状态存在哪里?""状态在传递中如何转化?"过程哲学:怀特海
"如何解耦?""边界在哪里消融?"禅宗:能所双亡

水的架构:流处理模式

一个典型的水架构是流处理模式:

// 输入 → [过滤] → [映射] → [聚合] → [输出]
public class DataPipeline
{
    private readonly IEnumerable<IFilter> _filters;
    private readonly IEnumerable<ITransformer> _transformers;
    private readonly IAggregator _aggregator;
    private readonly IOutputter _outputter;
    
    public DataPipeline(
        IEnumerable<IFilter> filters,
        IEnumerable<ITransformer> transformers,
        IAggregator aggregator,
        IOutputter outputter)
    {
        _filters = filters;
        _transformers = transformers;
        _aggregator = aggregator;
        _outputter = outputter;
    }
    
    public async Task ProcessAsync(IEnumerable<DataItem> items)
    {
        var result = items
            .Where(item => _filters.All(filter => filter.Apply(item)))
            .Select(item => _transformers.Aggregate(item, (current, transformer) => transformer.Apply(current)))
            .Aggregate(_aggregator.InitialValue, (acc, item) => _aggregator.Accumulate(acc, item));
        
        await _outputter.OutputAsync(result);
    }
}

这种架构的特点是:

  • 没有中心控制器,只有管道和数据流动
  • 每个组件只做一件事,做好它
  • 组件之间通过数据流连接,而非强耦合

版本管理的哲学视角

回到最初的问题:如何管理不断增长的版本数据?从水的视角来看,我们可以:

  1. 内容寻址 + 去重:像水一样识别并复用相同的内容
  2. 增量存储:像水流一样只记录变化
  3. 分层索引:像海洋分层一样管理热、温、冷数据
  4. 时间分片 + 合并压缩:像呼吸一样定期整理
  5. 引用计数 + 垃圾回收:像新陈代谢一样淘汰无用数据

实战代码示例

public interface IVersionManager
{
    Task<VersionId> CreateVersionAsync(Stream content);
    Task<Stream> GetVersionAsync(VersionId id);
    Task CleanupAsync();
}

public class VersionManager : IVersionManager
{
    private readonly IContentStore _contentStore;
    private readonly IIndexStore _indexStore;
    private readonly IRhythmOfOblivion _rhythm;
    
    public VersionManager(
        IContentStore contentStore,
        IIndexStore indexStore,
        IRhythmOfOblivion rhythm)
    {
        _contentStore = contentStore;
        _indexStore = indexStore;
        _rhythm = rhythm;
    }
    
    public async Task<VersionId> CreateVersionAsync(Stream content)
    {
        var hash = await _contentStore.CalculateHashAsync(content);
        var contentId = await _contentStore.StoreAsync(hash, content);
        var versionId = VersionId.New();
        
        await _indexStore.SaveVersionMappingAsync(versionId, contentId);
        return versionId;
    }
    
    public async Task<Stream> GetVersionAsync(VersionId id)
    {
        var contentId = await _indexStore.GetContentIdAsync(id);
        return await _contentStore.RetrieveAsync(contentId);
    }
    
    public async Task CleanupAsync()
    {
        await _rhythm.BreatheAsync();
    }
}

public interface IRhythmOfOblivion
{
    Task BreatheAsync(); // 呼吸:压缩、归档、GC
}

回应质疑:水代码的严谨性

有人可能会质疑:"水没有形式,无法验证,无法推理,不严谨。"但实际上:

  • λ演算(函数式)的β归约,本身就是流
  • 范畴论的态射合成,本身就是水的路径
  • 拓扑学的连续性,本身就是无形之有

数学的深处,与水相遇。水代码不是无序的,而是遵循更深刻的规律。

实践建议:下次写代码时

  1. 不写class,只写function:体验函数组合的威力
  2. 不存状态,只传状态:感受数据流的流动
  3. 不try-catch,用Result/Either类型:让错误自然流走
  4. 问三次:"这是石头,还是水?":培养水的思维

结语

李小龙说:"I fear not the man who has practiced 10,000 kicks once, but the man who has practiced one kick 10,000 times."(我不怕练过一万种踢法的人,只怕把一种踢法练过一万次的人。)

在代码世界里,同样如此。不怕写过一万个类的程序员,只怕把一种流动写过一万次的人。

让我们像水一样编程,让代码在流动中展现出生命的活力。