1、单一职责
定义:一个类应该只有一个变化的原因。功能和职责单一
特点:降低类的复杂性,易于管理
反例:
interface IUser{
String getName()
String getAge()
String getPassword()
setName(String name)
setAge(String age)
setPassword(String password)
changePassword()
deleteUser()
}
这段代码中用户的属性和行为没有分开,不符合单一原则
改:
interface IUser{
String getName()
String getAge()
String getPassword()
setName(String name)
setAge(String age)
setPassword(String password)
}
interface IUserBiz{
changePassword()
deleteUser()
}
这样使用户的属性和行为进行区分。
2、里氏替换原则
定义:只要是父类可以出现的地方子类就可以出现
四个含义:
a:子类必须完全实现父类的方法
b:子类可以有自己的个性
c:覆写或实现父类方法时,参数可以被“放大”
d:覆写或者实现父类方法时,输出结(返回值)可以被缩小
使用注意事项:
在开发中采用里氏替换原则时,尽量避免子类“个性”.
一旦子类拥有“个性”时,把子类当父类用就抹杀了子类的个性;
单独使用子类时会让代码耦合关系变得不明确,缺乏替换标准。
a:子类必须完全实现父类的方法
例
//接口类
public abstract class AbstractGun{
public abstarct void shoot();
}
//实现类
public class HandGun extends AbstractGun{
public void shoot(){
System.out.println("手枪射击");
}
}
public class Rifle extends AbstractGun{
public void shoot(){
System.out.println("步枪射击");
}
}
public class MachineGun extends AbstractGun{
public void shoot(){
System.out.println("机枪射击");
}
}
//士兵类
public class Soilder{
private AbstractGun gun;
public void setGun(AbstractGun gun){
this.gun = gun;
}
public void killEnemy(){
System.out.println("士兵拿出枪来啦!");
gun.shoot();
}
}
//实际使用
public class client{
public static void main(String[] args){
Solider sanMao = new Solider();
sanMao.setGun(new MachineGun());
sanMao.shoot();
}
}
当使用时,根据你传入的对象不同会做出不同的操作;
在编写时士兵不需要知道传入的是哪个Gun。只需要在使用时传入就可以了。
当子类不能完整的实现父类方法时建议断开关系,选择依赖、聚集、组合等关系代替
例:这是有个玩具枪
public class ToyGun extends AbstractGun{
public void shoot(){
//玩具枪是不具备射击功能的,所以这样继承并不合适
}
}
玩具枪是不具备射击功能的,所以这样继承并不合适
//接口类
public abstract class AbstractToyGun extends AbstractGun{
public abstarct void toyShoot();
}
public class ToyGun extends AbstractToyGun {
public void toyShoot() {
System.out.println("滋水枪");
}
}
当按照上面设计时,这样更符合使用设计,形状和声音托管给真正枪,但是玩具枪不具备射击功能,所以士兵拿枪时候会报错
public class Client {
public void main(String[] args){
Solider sanMao = new Solider();
sanMao.setGun(new ToyGun());//会报错
}
}
b:子类可以有自己的个性
例:
//狙击枪
public AUG extends Rifle{
public void zoomOut(){
System.out.println("AUG瞄准");
}
public void shoot(){
System.out.println("AUG射击");
}
}
//狙击手
public class AUGSnipper extends Soilder{
//狙击手射击依赖于狙击枪
public void killEnemy(AUG aug){
aug.zoomOut();
aug.shoot();
}
}
//实际使用
public class client{
public static void main(String[] args){
Solider sanMao = new AUGSnipper();
sanMao.setGun(new AUG());
sanMao.shoot();
}
}
***c:覆写或实现父类方法时,参数可以被“放大”(不能缩小)***
避免子类没有覆盖父类方法时,子类替换父类位置,子类方法却被执行的情况(方法重载情况)
***d:覆写或者实现父类方法时,输出结(返回值)可以被缩小(不能放大)***
3、依赖倒置原则
定义:更精简就是“面向接口编程”
a:高层模块不该依赖底层模块,两者都应该依赖于抽象
b:抽象不应该依赖于细节
c:细节应该依赖于抽象
依赖倒置的意思其实就是依赖于实现类的时候,选择去依赖抽象,倒置依赖关系
优点:依赖倒置原装减少了类与类之间的强耦合
使用注意事项:
每个类尽量都有接口类或者抽象类
变量的表面类型尽量是接口过着抽象类
任何类都不应该从具体类派生
尽量不去覆写基类方法
结合里氏替换原则使用
public interface IDriver{
public void driver(ICar car);
}
public interface ICar{
public void run();
}
public class BMW implements ICar{
public void run(){
System.out.println("宝马跑了~")
}
}
public class Benz implements ICar{
public void run(){
System.out.println("奔驰跑了~")
}
}
public class Client{
public static void main(String[] args){
IDriver sanMao = new Driver();
ICar bmw = new BMW();
sanMao.driver(bmw);
}
}
//测试类
public class DriverTest extends TextClass{
Mockery context = new JUni4Mockery();
@Test
public void testDriver(){
final Icar car = context.mock(ICar.class);
IDriver driver = new Driver();
context.checking(new Expectation(){{
oneOf(car).run();
}});
driver.driver(car);
}
}
测试时只需要一个接口就能完成测试
三种写法:
1、构造函数传递依赖对象
public interface IDriver{
public void driver()
}
public class MyDriver implements IDriver{
private ICar car;
public MyDriver(ICar _car){
this.car = _car;
}
public void driver(){
car.run();
}
}
2、set方法传递依赖对象
public class MyDriver implements IDriver{
private ICar car;
public void setCar(ICar _car){
this.car = _car;
}
public void driver(){
car.run();
}
}
3、在接口方法中声明依赖对象
public interface IDriver{
public void driver(ICar car);
}
public class MyDriver implements IDriver{
public void driver(ICar car){
car.run();
}
}
4、接口隔离原则
定义:客户端不应该依赖它不需要的接口;
类的依赖关系应该建立在最小的接口上。
简单来说就是接口方法应该尽量少,尽量细化
原则:
接口内要高内聚(不依赖其他接口)
定制服务,单独的为某一个功能而添加的接口,要查分
接口的设计是有限度的,过多的接口会造成结构复杂,开发难度增大
反例:
//美女接口
public interface IPettyGirl{
//好看
public void goodlooking();
//身材好
public void niceShape();
//气质好
public void greatTemperament();
}
//美女在这里
public class PettyGirl implements IPettyGirl{
String name;
public PettyGirl(String _name){
this.name = _name;
}
public void goodlooking(){
System.out.println("好看的脸蛋");
}
public void niceShape(){
System.out.println("好身材");
}
public void greatTemperament(){
System.out.println("气质棒棒哒");
}
}
//找美女抽象
public abstract class AbstractSearcher{
provited IPettyGirl pettyGirl;
public AbstractSrarch(IPettyGirl _pettyGirl){
this.pettyGirl = _pettyGirl;
}
public void show();
}
//找美女
public Searcher entends AbstractSearcher{
public Searcher(IPettyGirl _pettyGirl){
super_pettyGirl);
}
public void show(){
super.pettyGirl.goodlooking();
super.pettyGirl.niceShape();
super.pettyGirl.greatTemperament();
}
}
这段代码中定义了美女的类型,但是有一点就是现在的美女定义有区别(气质美女,和非气质美女),这样就造成了接口过大,所以这个接口是应该拆分的
改
//美女接口
public interface IGoodBodyGirl{
//好看
public void goodlooking();
//身材好
public void niceShape();
}
public interface IGreatTemperamentGirl{
//气质好
public void greatTemperament();
}
//美女在这里
public class PettyGirl implements IPettyGirl,IGreatTemperamentGirl{
String name;
public PettyGirl(String _name){
this.name = _name;
}
public void goodlooking(){
System.out.println("好看的脸蛋");
}
public void niceShape(){
System.out.println("好身材");
}
public void greatTemperament(){
System.out.println("气质棒棒哒");
}
}
这样在定义美女的时候就可以分别定义气质美女,和非气质美女了,而不会造成接口的修改
5、迪米特法则
定义:只与直接耦合关系的类进行通信
限制了类与类之间的耦合关系,要求类间解耦、弱耦合,提高复用率减少系统复杂程度
反例:
//老师类
public class Teacher{
//老师指挥课代表清点人数
public void commond(GroupLeader groupLeader){
List listGirls = new ArrayList();
for(int i = 0; i<20;i++){
listGirls.add(new Girl());
}
groupLeader.countGirls();
}
}
//女生类(只需要一个空类)
public class Girl{
}
//课代表
public class GroupLeader{
public void countGirls(List<Girl> listGirls){
System.out.println("女生的人数===>"+listGirls.size())
}
}
//使用
public class Client{
public voic main(String[] args){
Teacher teacher = new Teacher();
teacher.commond();
}
}
这个例子中teacher类中耦合的女生类明显是不必要的 改
public class Teacher{
public void commond(GroupLeader groupleader){
groupLeader.countGirls();
}
}
public class GroupLeader{
private List<Girl> listGirls;
public GroupLeader(List<Girl> _listGirls){
this.listGirls = _listGirls;
}
public void countGirls(){
System.out.println("女孩儿的人数为===>"+listGirls.size());
}
}
尽量减少public向外暴露,尽量的高内聚代码不依赖其他类
反例
//安装类
public class Wizard{
private Random random = new Random(System.ourrentTimeMillis());
public int first(){
System.out.println("第一步");
return random.nextInt(100);
}
public int second(){
System.out.println("第二步");
return random.nextInt(100);
}
public int third(){
System.out.println("第三步");
return random.nextInt(100);
}
}
//安装导向类
public class Insrall{
public void installWizard(Wizard wizard){
int first = wizard.first();
if(first>50){
int second = wizard.second();
if(second>50){
int third = wizard.third();
if(third>50){
System.out.println("安装完成");
}
}
}
}
}
//使用
public class Client{
public void main(String[] args){
Install install = new Install();
install.installWizard(new Wizard());
}
}
改
public class InstallWizard{
private Random random = new Random(System.ourrentTimeMillis());
private int first(){
System.out.println("第一步");
return random.nextInt(100);
}
private int second(){
System.out.println("第二步");
return random.nextInt(100);
}
private int third(){
System.out.println("第三步");
return random.nextInt(100);
}
public void installWizard(){
this.first();
if(first>50){
this.second();
if(second>50){
this.third();
if(third>50){
System.out.println("安装完成");
}
}
}
}
}
原则:如果一个方法放在本类既不增加类间关系,也不会对本类产生负面影响,则放置在本类中
谨慎使用 Serializable 修改容易导致序列化失败问题
6、开闭原则
等我足够的理解了再写
我一直都知道是对扩展开放对修改关闭,但是貌似只是个口号,从来都不知道如何实现
实现方案:
1、抽象约束
2、元数据控制模块行为
元数据指的是基础数据,配置参数、环境等的数据
3、制定项目章程
4、封装变化