软件的多维视图(略)
软件的开发过程
- 敏捷开发过程与传统开发过程对比:重开发轻文档、重交流轻合同、重结果清过程、重变化轻计划
git相关总结
- git和传统开发工具的区别:存的是文档而不是改变,所以可以创建任意分支
- git每个版本可以有几个父节点(0,1,2)
0:根节点,初始化
1:commit生成一个新版本
2:merge合并时二变一 - git经典命令
- commit -m 参数为注释
- checkout -b 参数为建立新的本地分支名(并切换)
- git init 初始化
- git config 配置项
- git clone 下载
- git remote add [shortname] [url] 增加一个新的远程仓库并命名
- git brach [branch-name] 新建一个分支,但依然停留在当前分支
- git brach -a 列出所有本地和远程分支
- git checkout [branch-name] 切换到指定分支,并更新工作区
- git add . 添加当前目录的所有文件到暂存区
- git commit -m [message] 提交暂存区到仓库区
- git merge [branch] 合并指定分支到当前分支(不切换分支)
- git push [remote] [branch] 上传本地指定分支到远程仓库
- git pull
- git push
代码快照图(略)
Immutability和mutability
- Immutable 不变数据类型:一旦被创建,其值不能改变。如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变
可变对象:拥有方法可以修改自己的值/引用
使用不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收)
可变类型最少化拷贝以提高效率,使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据
不可变类型更“安全”,在其他质量指标上表现更好,如果使用不可变类型,可以节省了频繁复制的代价
安全的使用可变类型:局部变量,不会涉及共享;只有一个引用
如果有多个引用(别名),使用可变类型就非常不安全 - 判断是不是Immutable类型,如何寻找
- 从表暴露角度:
看属性,属性不能有public
看return和set:
return不能将mutable类型引用传出
set不能将mutable类型引用传入 - 再找方法中有无对属性直接操作的方法
- 从表暴露角度:
- equal重写 举例:判断person对象的name age gender是否相等
public boolean equals(Object obj){
if (this == obj){
return true;
}
if (obj instanceof Person){
Person p = (Person)obj;
flag = this.name.equals(p.name)&&this.age.equals(p.age)&&this.gender.equals(p.gendder);
return flag;
}
return false
}
重写和重载
- 重写(运行阶段):
要求方法名相同,参数类型相同
子类返回类型小于等于父类返回类型
子类抛出异常小于等于父类抛出异常
子类访问权限大于等于父类访问权限
严格继承:子类只能添加新方法,无法重写超类中的方法
如果一个方法不能在Java程序中被覆盖,那么它必须以关键字final作为前缀 - 重载(编译阶段)
方法名相同,参数不同(参数数量、参数类型、参数顺序)interface Animal{ void vocalize(); } class Dog implements Animal{ public void vocalize(){ Symtem.out.prinfln("wolf!"); } } class Cow implements Animal{ public void vocalize(){ moo(); } public void moo(){Symtem.out.prinfln("Moo!");} } Animal a = new Animal(); //Compile: Cannot Instantiate the type Animal b.moo(); //Compile: The method moo() is undefined for the type Animal
AF/RI
- AF (Abstraction function) 数据会以什么样的方式映射
- RI (Rep invariant) 程序在执行的任何时间,属性需要满足的不变性条件
可复用性和可维护性
- (重写时要判断是否满足Liskov可替换原则)
知识点补充:co-variance、contra-variance为协变和逆变
协变指能够使用比原始指定的派生类型更具体的类型,逆变指能够使用比原始指定的派生类型的更不具体类型。
子类型可以增加方法,但不能删
子类型需要实现抽象类型中所有为实现的方法
子类型中重写的方法必须相同或子类型的返回值或者更小(强化后置条件)
子类型中重写的方法必须使用同样类型的参数或更大(弱化前置条件)
子类型中重写方法不能抛出额外的异常
正则表达式(前篇已总结)
七种设计模式继承or委托?
设计模式 | 继承(白盒复用)or委托(黑盒复用) |
---|---|
适配器模式(adapter) | an Adapter object —Delegation |
装饰器模式(decorator) | Decorators use both subtyping and delegation |
策略模式(strategy) | 通过delegation建立两个对象的动态联系 |
模板模式(template) | 使用继承和重写实现模板模式 |
迭代器模式(iterator) | 主要是通过委托来完成迭代 |
工厂模式(factory method) | |
访问者模式(visitor) | 通过delegation建立两个对象的动态联系 |
七种设计模式原理流程:
- 简单工厂模式:由一个工厂对象决定创建出哪一个产品的实例定义了一个创建对象的类,由这个类来封装实例化对象的行为
// 创建工厂类
public class SimpleFactory{
public Pizza createPizza(Sring orderPizza){
Pizza pizza = null;
if (orderType.equals("cheese")){
pizza = new GreekPizza();
// 省略其他对象
}
}
}
// 定义一个简单工厂对象
SimpleFactory simpleFactory;
Pizza pizza = null;
public OrderPizza(SimpleFactory simplefactory){
setFactory(simpleFactory);
}
public void setFactory(SimpleFactory simpleFactory){
String orderType = "";
this.simpleFactory = simpleFactory
do {
orderType = getType();
pizza = this.simpleFactory.createPizza(oderType);
if (pizza != null){
//...代码省略
}
}while(true);
}
// 订购客户端
public class PizzaStore{
public static void main(String[] args){
new OrderPizza(new SimpleFactory());
}
}
- 适配器模式
将一个类的接口转换成另一个类的接口,让原本接口不兼容的类可以兼容
// 被适配者
public class Voltage220V{
public int output220V(){
int src = 220V;
return src
}
}
// 适配接口
public interface IVoltage5V{
public int output5V();
}
// 适配器
public class VoltageAdaper extends Voltage220V implements IVotage5V{
@override
public int output5V(){
int src = output220V();
int detV = srcV / 44;
return detV
}
}
// Phone
public class Phone {
public void charging(Ivoltage5V iVoltage5V)
}
// 客户端
public class Client{
public static void main(String[] args){
Phone phone = new Phone();
phone.charging(new VoltageAdapter())
}
}
- 模板模式:模板模式定义一个操作中的算法股价,而将一些步骤延迟到子类中,使得子类可以不改变一个算法的结构而重新定义某些步骤
// 定义一个抽象类包括所需要的模板信息,非模板内容空出交给子类
// 子类继承模板,并重写需要的对应的需要子类实现的部分
// 代码略
- 访问者模式:将数据结构和数据操作相分离,解决数据结构和操作耦合性问题,所需角色如下
(1)Visitor是抽象访问者,为该对象结构中的ConcreteElement的每个类声明一个visit操作
public class Person{
// 代码略
}
(2)ConcreteVistor是一个具体的访问值,实现每个Vistor声明的操作,是每个操作实现的部分
public abstract class Action{
public abstract void getManResult(Man man)
}
(3)ObjectStructure能枚举它的元素,可以提供一个高层的接口,用来允许访问者访问元素
public class ObjectStrcture{
private List<Person> persons = new LinkedList<>();
public void attach(Person p){
persons.add(p);
}
public void detach(Person p){
persons.remove(p);
}
}
(4)Element定义一个accept方法,接收一个访问者对象
public abbstract class Person{
public abstract void appect(Action action);
}
(5)ConcreteElement为具体元素,实现了accept方法
public class Man extends Person {
public void accept(Action action){
action.getManResult(this);
}
}
- 迭代器模式
提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即不暴露其内部结构
需要重写系统提供的迭代器的hasNext()/next()/remove()方法 - 策略模式
定义算法族,分别封装起来,让他们之间可以相互替换(不同策略采取不同接口)——方法策略角度
例如使用一个flyBehavor接口聚合不同的叫声,再flyBehavior = new GoodFlyBhabior()等
- 装饰者设计模式:动态的将进功能加到对象上,我的理解是继承+组合+嵌套
七种设计模式使用范围
- 当我们会用到大量的创建某种、某类或某批对象时,就会使用到工厂模式
- 需要对一个对象结构中的对象进行很多不同的操作(这些操作彼此没有关联),同时需要避免让这些操作“污染”这些对象的类,可以使用访问者模式实现
- 访问者模式是程序具有了优秀的可扩展性、灵活性非常高,适用于数据结构稳定且功能需求变化
- 迭代器模式:需要一个方法遍历 List||Array
- 装饰者模式可以解决类爆炸的问题,动态的将进功能加到对象上,功能的扩充/修剪常用
- 策略模式:在不用方法之间就进行灵活切换
测试
- 白盒测试无法做到完备性测试
- 测试覆盖程度:函数覆盖->语句覆盖->分支覆盖 条件覆盖->路径覆盖
- 黑盒测试:只能根据需求对接口进行测试
- 等价类划分:完备+异常+边界值
异常
- 编写捕获
try catch 捕获异常
try {
// 可疑代码,将异常生成对应的异常对象,传递给catch块
} catch(e){
// 对异常的处理
}
throw 抛出异常,要在方法部分对抛出的异常进行声明(@throws)
if (){
throw new xxxException("提示信息")
}
class xxxException extends RuntimeException{
public xxxException(String message){
super(message);
}
}
实现和继承
- 接口可以多继承,类不能多继承
- 接口中可以放defalt和静态方法的具体实现
final
- 加在方法:方法不能被重写
- 加在值:值引用不能被改变
- 加载类:类不能被继承
RI书写
- 属性本身的影响
- 属性之间的相互影响
泛型
- 类型通配符下限通过形如 List<? super Number> 来定义,表示类型只能接受 Number 及其上层父类类型,如 Object 类型的实例。