最近有人问我 系统设计的原则,事实上不论今天各个技术栈怎么演化,那些本质的原则与方法不会变, 让我们回顾一下 这些原则:
•分散关注 Separation of concerns. Divide your application into distinct features with as little overlap in functionality as possible. The important factor is minimization of interaction points to achieve high cohesion and low coupling. However, separating functionality at the wrong boundaries can result in high coupling and complexity between features even though the contained functionality within a feature does not significantly overlap. 不同领域的功能,应该由不同的代码和最小重迭的模块组成。
•单一职责,功能高内聚 Single Responsibility principle. Each component or module should be responsible for only a specific feature or functionality, or aggregation of cohesive functionality.
•一个模块不需要知道另一个模块的内部细节 Principle of Least Knowledge (also known as the Law of Demeter or LoD). A component or object should not know about internal details of other components or objects.
•Don’t repeat yourself (DRY). You should only need to specify intent in one place. For example, in terms of application design, specific functionality should be implemented in only one component; the functionality should not be duplicated in any other component.
•不要过分设计过多模块 Minimize upfront design. Only design what is necessary. In some cases, you may require upfront comprehensive design and testing if the cost of development or a failure in the design is very high. In other cases, especially for agile development, you can avoid big design upfront (BDUF). If your application requirements are unclear, or if there is a possibility of the design evolving over time, avoid making a large design effort prematurely. This principle is sometimes known as YAGNI ("You ain’t gonna need it").
软件设计 中 SOLID原则
The Single Responsibility Principle
There should never be more than one reason for a class to change. Basically, this means that your classes should exist for one purpose only. Responsibility is the heart of this principle, so to rephrase there should never be more than one responsibility per class.
The Open Closed Principle
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification. At first, this seems to be contradictory: how can you make an object behave differently without modifying it? The answer: by using abstractions, or by placing behavior(responsibility) in derivative classes. In other words, by creating base classes with override-able functions, we are able to create new classes that do the same thing differently without changing the base functionality. Further, if properties of the abstracted class need to be compared or organized together, another abstraction should handle this. This is the basis of the "keep all object variables private" argument.
The Liskov Substitution Principle
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it. In other words, if you are calling a method defined at a base class upon an abstracted class, the function must be implemented properly on the subtype class. Or, "when using an object through its base class interface, [the] derived object must not expect such users to obey preconditions that are stronger than those required by the base class."Turns out a square is not a rectangle, at least behavior-wise.
The Interface Segregation Principle
Clients should not be forced to depend upon interfaces that they do not use. My favorite version of this is written as "when a client depends upon a class that contains interfaces that the client does not use, but that other clients do use, then that client will be affected by the changes that those other clients force upon the class."
The Dependency Inversion Principle
Depend on abstractions, not on concretions or High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions. This is very closely related to the open closed principle we discussed earlier.
从面向对象OO方向出发,也是设计模式的思想有:
封装变化点
该原则的核心思想是,在程序设计中找出应用中可能需要变化之处,把它们独立出来以便以后可以轻易的改动或者扩充,而不影响不需要变化的部分, 哪儿变化就封装哪儿
面向接口编程
接口提供了非常优秀的抽象归纳,让我们的开发工作变得容易很多。一般实现一个系统的时候,通常是将定义与实现合为一体,不加分离的,认为最为理想的系统设计规范应是所有的定义与实现分离,尽管这可能对系统中的某些情况有点麻烦。
使用组合而不是继承
继承破坏封装性。基类的很多内部细节都是对派生类可见的,因此这种复用是“白箱复用”;如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。继承关系有很多缺点,如果合理使用组合则可以有效的避免这些缺点,使用组合关系将系统对变化的适应力从静态提升到动态,而且由于组合将已有对象组合到了新对象中,因此新对象可以调用已有对象的功能。由于组合关系中各个各个对象的内部实现是隐藏的,我们只能通过接口调用,因此我们完全可以在运行期用实现了同样接口的另外一个对象来代替原对象,从而灵活实现运行期的行为控制。而且使用合成关系有助于保持每个类的职责的单一性,这样类的层次体系以及类的规模都不太可能增长为不可控制的庞然大物。因此我们优先使用组合而不是继承。 当然这并不是说继承是不好的,我们可用的类总是不够丰富,而使用继承复用来创建一些实用的类将会不组合来的更快,因此在系统中合理的搭配使用继承和组合将会使你的系统强大而又牢固。
还有这些也很重要
Simplicity (KISS)
The most important principle is keeping things simple. 简单是软件设计的目标,简单的代码占用时间少,漏洞少,并且易于修改。Simplicity should be your northern star, your compass, and your long-term commitment. Keeping software simple is difficult because it is inherently relative. There is no standardized measurement of simplicity, so when you judge what is simpler, you need to first ask yourself for whom and when. For example, is it simpler for you or for your clients? Is it simpler for you to do now or maintain in the future?
低耦合原则(Minimize Coupling)
代码的任何一个部分应该减少对其他区域代码的依赖关系。尽量不要使用共享参数。低耦合往往是完美结构系统和优秀设计的标志
Designing for scale
Designing for scale is a difficult art, and each technique described in this section comes with some costs. As an engineer, you need to make careful tradeoffs between endless scalability and the practicality of each solution. To make sure you do not over engineer by preparing for scale that you will never need, you should first carefully estimate the most realistic scalability needs of your system and design accordingly.
Design for Self-Healing
The final design principle in this chapter is designing software for high availability and self-healing. A system is considered to be available as long as it performs its functions as expected from the client's perspective. It does not matter if the system is experiencing internal partial failure as long as it does not affect the behavior that clients depend on. In other words, you want to make your system appear as if all of its components were functioning perfectly even when things break and during maintenance times.
Designing For Failure
Each application component must be deployed across redundant cloud components, ideally with minimal or no common points of failure
Each application component must make no assumptions about the underlying infrastructure—it must be able to adapt to changes in the infrastructure without downtime
Each application component should be partition tolerant—in other words, it should be able to survive network latency (or loss of communication) among the nodes that support that component
Automation tools must be in place to orchestrate application responses to failures or other changes in the infrastructure
一些设计的实践
Keep design patterns consistent within each layer. Within a logical layer, where possible, the design of components should be consistent for a particular operation. For example, if you choose to use the Table Data Gateway pattern to create an object that acts as a gateway to tables or views in a database, you should not include another pattern such as Repository, which uses a different paradigm for accessing data and initializing business entities. However, you may need to use different patterns for tasks in a layer that have a large variation in requirements, such as an application that contains business transaction and reporting functionality.
Do not duplicate functionality within an application. There should be only one component providing a specific functionality—this functionality should not be duplicated in any other component. This makes your components cohesive and makes it easier to optimize the components if a specific feature or functionality changes. Duplication of functionality within an application can make it difficult to implement changes, decrease clarity, and introduce potential inconsistencies.
Prefer composition to inheritance. Wherever possible, use composition over inheritance when reusing functionality because inheritance increases the dependency between parent and child classes, thereby limiting the reuse of child classes. This also reduces the inheritance hierarchies, which can become very difficult to deal with.
Establish a coding style and naming convention for development. Check to see if the organization has established coding style and naming standards. If not, you should establish common standards. This provides a consistent model that makes it easier for team members to review code they did not write, which leads to better maintainability.
Maintain system quality using automated QA techniques during development. Use unit testing and other automated Quality Analysis techniques, such as dependency analysis and static code analysis, during development. Define clear behavioral and performance metrics for components and sub-systems, and use automated QA tools during the build process to ensure that local design or implementation decisions do not adversely affect the overall system quality.
Consider the operation of your application. Determine what metrics and operational data are required by the IT infrastructure to ensure the efficient deployment and operation of your application. Designing your application’s components and sub-systems with a clear understanding of their individual operational requirements will significantly ease overall deployment and operation. Use automated QA tools during development to ensure that the correct operational data is provided by your application’s components and sub-systems.
在互联网系统下产生的一些原则
1.避免单点故障:任何东西都要有两个。这增加了成本和复杂度,但却能在可用性和负载性能上获益。而且,这有助于设计者采用一种分布式优先的思维。可(异地)部署和就近路由接入,破除单点故障;可分布,可调度的原则
2.横向扩展,而不是纵向扩展:升级服务器(纵向)的成本是指数增长的,而增加另一台商用服务器(横向)的成本是线性增长的。
3.尽量减少应用程序核心所需要完成的工作。4.API优先:将应用程序视为一个提供API的服务,而且,不假定服务的客户端类型(手机应用、Web站点、桌面应用程序)。
5.提供尽可能新的数据:用户可能不需要立即看到最新的数据,最终一致性可以带来更高的可用性。
6.设计时要考虑维护和自动化:不要低估应用程序维护所需要的时间和工作量。软件首次公开发布是一个值得称赞的里程碑,但也标志着真正的工作要开始了。
7.为故障做好准备:将故障对终端用户的影响最小化。
8.数据上报和监控平台;
用户行为数据,系统性能监控数据,系统异常和业务相关数据等的上报
9.数据分级存储原则:单内存cache存储,内存cache+异步更新,内存cache+同步更新;
从三个纬度分析用户行为模型,决定相关数据的存储策略:1),能忍受用户数据的丢失吗?2),能忍受数据的非及时性吗? 3),数据的读写比例分布如何?
10.动静分离原则;
能静态化尽量静态化,在代码和进程部署上,在DNS层上做好动静分离的系统设计准备
11.轻重分离原则;
保持接入和业务处理的分离,接入尽量轻量化,使得系统具有很好的吞吐量,处理尽量异步化,使得可以平滑扩展
12. 破除服务依赖原则:同一IDC的其他服务对系统的影响,第三方调用系统接口的隔离和过载保护,依赖第三方服务的监控和安全保护原则等。
13.柔性可用原则;
处理好异常情况下的灰度体验,区分好关键处理路径和非关键路径,而系统设计要尽量把关键路径转换成非关键路径
14.异步化,能异步的尽量异步原则;
通过内存管道,操作流水等技术进行拼接各个处理模块
15.灰度原则;
灰度发布策略是根据用户号码段,用户ip段,还是用户vip等级,用户所在城市等进行灰度升级,保证系统的平滑迭代
16.异常的快速响应和一键切换原则;
IDC断电?系统切换到正常的成本是多少?时间呢?需要几个人操作?牛的系统可以一个人在管理后台按一个按钮就可以切换,再按一下就可以切换回来
17.有损服务原则;
用低成本提供海量的服务原则
18.充分利用DNS层做好系统的可分布设计
19.区分系统行为和用户行为并分别进行设计,甚至在关键时刻可以进行转换。
20.努力实现无状态:状态信息要保存在尽可能少的地方,而且要保存在专门设计的组件中。坚持app_server设计的无状态设计原则,转变用户行为为系统行为,使得app_server具有无状态的特点
21.多级cache设计以及各个cache的路由设计
22.“大系统小做”原则
23.强事务模型到最终一致性事务模型的转换原则
24.尽可能拆分
25.服务架构“去中心化”
26.数据化运营
27.尽可能使用成熟组件
28.尽可能自动化
大规模web系统设计的一些核心原则:
可用性: 一个网站的正常运行时间对于许多公司的声誉与运作都是至关重要的。对于一些更大的在线零售站点,几分钟的不可用都会造成数千或数百万美元的营收损失,因此系统设计得能够持续服务,并且能迅速从故障中恢复是技术和业务的最基本要求。分布式系统中的高可用性需要仔细考虑关键部件的冗余,从部分系统故障中迅速恢复,以及问题发生时优雅降级。
性能: 对于多数站点而言,网站的性能已成为一个重要的考虑因素。网站的速度影响着使用和用户满意度,以及搜索引擎排名,与营收和是否能留住用户直接相关。因此,创建一个针对快速响应与低延迟进行优化的系统非常重要。
可靠性: 系统必须是可靠的,这样相同数据请求才会始终返回相同的数据。数据变换或更新之后,同样的请求则应该返回新的数据。用户应该知道一点:如果东西写入了系统,或者得到存储,那么它会持久化并且肯定保持不变以便将来进行检索。
可扩展性: 对于任何大型分布式系统而言,大小(size)只是需要考虑的规模(scale)问题的一个方面。同样重要的是努力去提高处理更大负载的能力,这通常被称为系统的可扩展性。可扩展性以系统的许多不同参数为参考:能够处理多少额外流量?增加存储容量有多容易?能够处理多少更多的事务?
可管理性: 系统设计得易于运维是另一个重要的考虑因素。系统的可管理性等价于运维(维护和更新)的可扩展性。对于可管理性需要考虑的是:问题发生时易于诊断与理解,便于更新或修改,系统运维起来如何简单(例如:常规运维是否不会引发失败或异常?)
成本: 成本是一个重要因素。很明显这包括硬件和软件成本,但也要考虑系统部署和维护这一方面。系统构建所花费的开发者时间,系统运行所需要的运维工作量,以及培训工作都应该考虑进去。成本是拥有系统的总成本。
剩下来 就可以围绕 ISO 9126质量模型:软件质量模型的6大特性和27个子特性 来进行系统设计与分析,度量, 他们是:
一、功能性:
1、适合性:提供了相应的功能
2、准确性:正确(用户需要的)
3、互操作性:产品与产品之间交互数据的能力
4、保密安全性:允许经过授权的用户和系统能够正常的访问相应的数据和信息,禁止未授权的用户访问.......
5、功能性的依从性:国际/国家/行业/企业 标准规范一致性二、可靠性:产品在规定的条件下,在规定的时间内完成规定功能的能力
1、成熟性:防止内部错误导致软件失效的能力
2、容错性:软件出现故障,自我处理能力
3、易恢复性:失效情况下的恢复能力
4、可靠性的依从性三、易用性:在指定使用条件下,产品被理解、 学习、使用和吸引用户的能力
1、易理解性:
2、易学性:
3、易操作性:
4、吸引性:
5、易用性的依从性:四、效率性:在规定台条件下,相对于所用资源的数量,软件产品可提供适当性能的能力
1、时间特性:平均事务响应时间,吞吐率,TPS(每秒事务数)
2、资源利用性:CPU 内存 磁盘 IO 网络带宽 队列 共享内存
3、效率依从性:五、软件维护性:"四规", 在规定条件下,规定的时间内,使用规定的工具或方法修复规定功能的能力
1、易分析性:分析定位问题的难易程度
2、易改变性:软件产品使指定的修改可以被实现的能力
3、稳定性:防止意外修改导致程序失效
4、易 测试性:使已修改软件能被确认的能力
5、维护性的依从性六、软件可移植性:从一种环境迁移到另一种环境的能力
1、适应性:适应不同平台
2、易安装性:被安装的能力
3、共存性:
4、易替换性
5、可移植性的依从性:
仔细回顾现在各类的系统平台,框架,组件,工程方法,都至少包含有上面的设计思想与原则。可能还有遗漏的,后续补充。
--------------------------------------------------------------------------------------------------------------------------------------------
今天先到这儿,希望对您在系统架构设计与评估,团队管理, 项目管理, 产品管理 有参考作用 , 您可能感兴趣的文章:
互联网电商购物车架构演变案例
互联网业务场景下消息队列架构
消息系统架构设计演进
互联网电商搜索架构演化之一
企业信息化与软件工程的迷思
企业项目化管理介绍
软件项目成功之要素
人际沟通风格介绍一
精益IT组织与分享式领导
学习型组织与企业
企业创新文化与等级观念
组织目标与个人目标
初创公司人才招聘与管理
人才公司环境与企业文化
企业文化、团队文化与知识共享
高效能的团队建设
项目管理沟通计划
构建高效的研发与自动化运维
某大型电商云平台实践
互联网数据库架构设计思路
IT基础架构规划方案一(网络系统规划)
餐饮行业解决方案之客户分析流程
餐饮行业解决方案之采购战略制定与实施流程
餐饮行业解决方案之业务设计流程
供应链需求调研CheckList
企业应用之性能实时度量系统演变
如有想了解更多软件设计与架构, 系统IT,企业信息化, 团队管理 资讯,请关注我的微信订阅号:
作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog。