设计模式之禅——6大设计原则

6个原则

  • 单一职责:有且仅有一个原因引起类的变更
  • 里氏替换:所有使用基类的地方都能无缝的使用其子类
  • 依赖倒置:所有依赖都应该依赖于抽象,细节依赖于抽象,抽象依赖于抽象,面向契约编程就是它的实现
  • 最少知识:一个对象应该对其他对象有最少了解,又名迪米特法则
  • 接口隔离:客户端不应该依赖他不需要的接口
  • 开闭原则:应该对修改关闭,对扩展开放

继续阅读“设计模式之禅——6大设计原则”

MetaWeblogApi for Hexo 开发纪录

为什么要开发MetaWeblogApi for hexo

  1. 讨厌每次都要敲命令行
  2. 想在任何地方编写文章并可以提交
  3. 喜欢编辑器沉浸式的编写体验

实现原理

participant 编辑器 as B
participant MetaWeblogApi as M
participant 博客Git地址 as A
participant 博客 as E

B->M: 发送Mardown格式的文本
note left of M: git pull 
M->B: 返回BlogId确认提交成功 
note right of M: 生成Hexo 的Markdown
M->A: 提交到
note right of M: 调用Hexo 生成
M->E: deploy 到博客地址

具体实现

外部访问Api实现部分

使用SpringBoot

  1. SpringMVC是目前Java最流行的框架
  2. Java是我本来的开发语言,趁手的才是最好的
  3. SpringBoot相对于原始的SpringMVC 更简单的配置

Metaweblog 的格式

Metaweblog的实现主要是参考这个开源项目,这个源码写得很直白,非常好懂。简单一点解释Metaweblog Api(后面简称M),和M对接的url其实只有一个,然后把访问不同的方法名封装在一个XML里面,通过解析这个xml,我们就能够知道这个请求到底需要访问什么方法。这个和我们一般的Api都不太一样,我在这个地方迷茫了好久。知道这个之后剩下的就是xml解析的问题了

使用Groovy XMLParser 解析xml

需要解析的xml

<?xml version="1.0"?>
<methodCall>
    <methodName>blogger.getUsersBlogs</methodName>
    <params>
        <param>
            <value>
                <string></string>
            </value>
        </param>
        <param>
            <value>
                <string>12</string>
            </value>
        </param>
        <param>
            <value>
                <string>12</string>
            </value>
        </param>
    </params>
</methodCall>

解析使用的groovy代码

methodCall = new XmlParser().parseText(text)
methodName = methodCall.methodName.text()
params = methodCall.params.param
mUserAccount = params[1].text()
mUserPassword = params[2].text()

使用groovy 解析的话就好像使用js一样,直接使用xml字段名就能获取到值,非常方便

Git的提交部分

JGit 是个Java实现的Git操作库,可以很方便的操作各种Git操作,我主要使用到以下几个方法

clone
mGit = Git.cloneRepository()
            .setURI(url)
            .setDirectory(new File(mGitConfig.repositoryLocation))
            .setTransportConfigCallback(mTransportConfigCallback)
            .setBranch('master')
            .setCloneSubmodules(true)
            .setTimeout(30000)
            .call()
update
def git = git()
git.pull().setTransportConfigCallback(mTransportConfigCallback).call()
git.submoduleUpdate().call()
 //如果不是干净的就合并
if (!git.status().call().clean) {
git.add().addFilepattern('.').call()          
git.commit().setMessage(buildCommitMessage(git.status().call())).call()
push
def pushResult = git().push()
                .setPushAll()
                .setTransportConfigCallback(mTransportConfigCallback)
                .call()

因为使用的giturl 提交,需要sshkey ,需要在~/.ssh/生成密钥

调用Hexo 部分

我重新拓展了groovy String 中的execute,使得每次执行命令都在博客的目录下

 String.metaClass.execute {
            if (new File(mConfigBean.repositoryLocation).exists()){
                delegate.execute(null,new File(mConfigBean.repositoryLocation))
            }else{
                delegate.execute(null,new File('/'))
            }

        }

但是我不建议这样做,这样写是方便了,但是会造成在别的地方执行execute会失败。建议是重新拓展一个方法。

因为使用groovy,我就可以这样执行hexo的命令了

初始化hexo

'npm install '.execute().text

生成和推送

'hexo generate -d'.execute()

docker封装

Docker 这个过程最蛋疼!!

  1. nodejs 要安装6版本
  2. npm不需要安装了,安装node的时候附带了

贴出构建Docker的Dockerfile

FROM java:8u111-jdk

MAINTAINER <Hangox,liang.hanguang93@gmail.com>

RUN apt-get update -y --no-install-recommends
RUN apt-get install -y --no-install-recommends git-core curl
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash -
RUN apt-get install -y  --no-install-recommends nodejs
RUN npm install hexo-cli -g

COPY . /app
WORKDIR /app
RUN ./gradlew assemble
RUN mv build/libs/MetaweblogApi-1.0.jar app.jar

ENV GIT_NAME HexoMetaApi
ENV GIT_EMAIL your@email.com
ENV ACCOUNT account
ENV PASSWORD password
ENV REPOSITORY ''
#
#VOLUME ["root/.ssh", "/hexo"]
#EXPOSE 9000

CMD java -jar app.jar \
        --blog.account=$ACCOUNT \
        --blog.password=$PASSWORD \
        --git.repository=$REPOSITORY \
        --git.name=$GIT_NAME \
        --git.email=$GIT_EMAIL

总结

SpringBoot 这个框架简化了很多SpringMVC的配置,搭配Groovy开发非常舒服。
Metaweblog api 的实现还在于很粗糙的程度,目前也只是实现了,newPosteditPost两个简单的操作,争取实现更多的操作。Docker构建的话,还是希望自己以后不要在智商掉线了。Docker的相关信息也是时候要总结一下了。

算法,两数之和

问题来源:two sum

问题描述

给定一个整数数组,返回两个数字的索引,使它们相加到一个特定的目标。 您可以假设每个输入都只有一个解决方案, 而您可能不会使用相同的元素两次。

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

正向思维解法

直接遍历所有的组合,求出问题的解

public int[] twoSum(int[] nums, int target) {
     for (int i = 0; i < nums.length; i++) {
           for (int j = i + 1; j < nums.length; j++) {
                    if (target == nums[j] + nums[i]) {
                        return new int[] { i, j };
                    }
         }
    }
    throw new IllegalArgumentException("No two sum solution");
}

分析:
这是最简单的解法。两个嵌套循环,时间复杂度为O(n2)。空间复杂度为O(1)

逆向思维解法

用目标减去加数1,得到加数2,再在数组中寻找。

实现1

public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i + 1; j < nums.length; j++) {
                if (target - nums[i] == nums[i]) {
                    return new int[] { i, j };
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }

分析
没错,就是和第一解法时间空间复杂度都是一样的😂。没事,我们可以优化一下的。

实现2

在实现一中,我们的瓶颈主要是在如何根据值去找到索引的问题上,所以这里我们使用Map去优化根据值去寻找位置的瓶颈

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        map.put(nums[i], i);
    }
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement) && map.get(complement) != i) {
            return new int[] { i, map.get(complement) };
        }
    }
    throw new IllegalArgumentException("No two sum solution");
}

分析:
我们使用一个循环把数据与位置建立索引,然后获取的时候直接从map中直接获取,这样就可以避免了两个嵌套的循环了,是的时间复杂度为O(n) ,空间复杂度为O(1).

note: 在算法中,我们不考虑map这些数据结构具体实现使用的时间

实现3

实现2中使用了两个循环,能不能把第一个循环也去掉呢?答案是可以的。一边循环,一边插入map,然后获取这个差值是否已经加入到map中。

public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int complement = target - nums[i];
        if (map.containsKey(complement)) {
            return new int[] { map.get(complement), i };
        }
        map.put(nums[i], i);
    }
    throw new IllegalArgumentException("No two sum solution");
}

分析:
这个复杂度和实现2是一样的,时间为O(n),空间为O(n),但细看还是有差别的,这个算法的实际上比实现2的快了1倍