在我们日常工作中,代码写着写着就出现下列的一些臭味。但是还好我们有SOLID这把‘尺子’, 可以拿着它不断去衡量我们写的代码,除去代码臭味。这就是我们要学习SOLID原则的原因所在。
一个很好的例子:在我们封装request库时,我们需要实现以下4个方法.
class MyRequestClient:
def post(self):
pass
def get(self):
pass
def update(self):
pass
def delete(self):
pass
#上面的方法就是属于同一职责。 如何还有其他的方法,那么这个类就不符合单一职责原则。
#例增加以下方法:
def get_db_data(self):
pass
def to_object(self):
pass
首先我们来看一个违反OCP原则的例子。
#bad code
def circle_draw():
print(f"this is circle draw")
def square_draw():
print(f"this is square draw")
def draw_all_shape(shapes):
for shape in shapes:
if shape == "circle":
circle_draw()
if shape == "square":
square_draw()
这段代码的问题是如果再有新的类型需要draw, 我们需要修改draw_all_shape函数来适配新的类型。
我们定义了一个抽象类Shape, 子类Square和Circle继承Shape. 并且在子类中重写了父类的方法。函数draw_all_shape是绘制所有图形。

from typing import List
from abc import ABCMeta, abstractmethod
class Shape(metaclass=ABCMeta):
@abstractmethod
def draw(self):
pass
class Square(Shape):
def draw(self):
print(f"this is square draw")
class Circle(Shape):
def draw(self):
print(f"this is circle draw")
def draw_all_shape(shapes: List[Shape]):
for shape in shapes:
shape.draw()
我们定义了一个抽象类Shape, 子类Square和Circle继承Shape. 并且在子类中重写了父类的方法。函数draw_all_shape是绘制所有图形。
def circle_draw():
print(f"this is circle draw")
def square_draw():
print(f"this is square draw")
def draw_all_shape_by_function(data: Dict[str,Callable]):
for key,value in data.items():
value()
data = {
"circle": circle_draw,
"square": square_draw
}
draw_all_shape_by_function(data=data)
Shape.无需修改shape类和draw_all_shape就可以实现三角形类的绘制。首先我们来看一段违法LSP的例子
from typing import Iterable
class User():
def __init__(self, user: str) -> None:
self.user = user
def disable(self) -> None:
print(f"{self.user} disable!")
class Admin(User):
def __init__(self, user: str = "Admin") -> None:
self.user = user
def disable(self):
raise "Admin do not disable!"
def delete_user(users: Iterable[User]):
for user in users:
user.disable()
当执行delete_user时,就会抛出TypeError 错误,Admin类中disable方法违法了LSP替换原则。
#Good
from typing import Iterable
class User():
def __init__(self, user: str) -> None:
self.user = user
def allow_disable(self):
return True
def disable(self) -> None:
print(f"{self.user} disable!")
class Admin(User):
def __init__(self, user: str = "Admin") -> None:
self.user = user
def allow_disable(self):
return False
def delete_user(users: Iterable[User]) -> None:
for user in users:
if user.allow_disable:
user.disable()
allow_disable 的方法,解决了Admin类不能disable的问题。首先来看一个违反ISP原则的例子:
class Animal(metaclass=ABCMeta):
@abstractclassmethod
def run(self):
pass
@abstractclassmethod
def speak(self):
pass
@abstractclassmethod
def fly(self):
pass
class Dog(Animal):
def run(self):
return "Dog Running"
def speak(self):
return "Dog Speaking"
def fly(self):
raise TypeError("Dog can not fly")
class Bird(Animal):
def run(self):
raise TypeError("Bird can not run")
def speak(self):
return "Bird Speaking"
def fly(self):
return "Bird fly"
def fly_animal(animals: Iterable[Animal]):
for animal in animals:
animal.fly()

当我们执行fly_animal时,就会抛出TypeError的错误。此时Animal抽象类是一个胖类,违法了ISP原则。

#good
class FlyingAnimal(metaclass=ABCMeta):
@abstractclassmethod
def fly(self):
pass
class RunningAnimal(metaclass=ABCMeta):
@abstractclassmethod
def run(self):
pass
class TalkingAnimal(metaclass=ABCMeta):
@abstractclassmethod
def talk(self):
pass
class Dog(RunningAnimal,TalkingAnimal):
def run(self):
return "Dog Running"
def talk(self):
return "Dog Speaking"
class Bird(FlyingAnimal, TalkingAnimal):
def talk(self):
return "Bird Speaking"
def fly(self):
return "Bird fly"
def fly_animal(animals: Iterable[FlyingAnimal]):
for animal in animals:
print(animal.fly())
首先看一个违反DIP原则的例子:
class Lamp:
def turn_on(self):
print("turn on the lamp")
def turn_off(self):
print("turn off the lamp")
class Button():
def __init__(self) -> None:
self.lamp = Lamp()
def turn_on(self):
return self.lamp.turn_on()
def turn_off(self):
return self.lamp.turn_off()
当有一天,button需要控制televsion时,就需要修改Button类。Button和Lamp 具有强耦合关系。所以,当Lamp变动时,会影响到Button类。违法了DIP原则的高层模块依赖于底层模块。


定义一个抽象类ElectricAppliance Button 和 Lamp 都依赖这个抽象类。 解决了Button和Lamp 具有强耦合的问题。
class ElectricAppliance(metaclass=ABCMeta):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
class Lamp(ElectricAppliance):
def turn_on(self):
print("turn on the lamp")
def turn_off(self):
print("turn off the lamp")
class Television(ElectricAppliance):
def turn_on(self):
print("turn on the televison")
def turn_off(self):
print("turn off the televison")
class Button:
def __init__(self, electric_appliance: ElectricAppliance) -> None:
self.electric_appliance = electric_appliance
def turn_on(self):
self.electric_appliance.turn_on()
def turn_off(self):
self.electric_appliance.turn_off()
我似乎经常遇到一些设计问题,但我不知道是什么是真的很合适。一方面我经常听到我应该限制耦合和坚持单一职责,但当我这样做时,我常常发现它很困难到在需要时将信息获取到程序的一部分。为了例如,classSingerdefinitialize(name)@name=nameendattr:nameend那么Song应该是:classSongdefnew(singer)@singer=singerendend或classSongdefnew(singer_name)@singer_name=singer_nameendend后者耦合性小,按道理应该用。但如果我以后发现宋有什么需要了解更多歌手,我的
我为你们准备了一个简单的。我想要一个特色内容部分,其中排除了当前文章所以这可以通过delete_if使用MiddlemanBlog:但是我使用的是中间人代理,所以我无法访问current_article方法...我有一个YAML结构,其中包含以下模拟数据(以及其他数据),文件夹设置如下:data>site>caseStudy>RANDOM-ID423536.yaml(由CMS生成)在每个yaml文件中,您会发现如下内容::id:2k1YccJrQsKE2siSO6o6ac:title:Heyplace我的config.rb看起来像这样data.site.caseStudy.eachdo
方法调用通常可以省略接收者和参数的括号:deffoo;"foo"endfoo#=>"foo"在上面的例子中,foo在方法调用和对潜在局部变量的引用之间是不明确的。在没有后者的情况下,它被解释为方法调用。但是,当方法名原则上可以是常量名时(即,当它以大写字母开头,并且仅由字母组成时),似乎需要消歧。defFoo;"Foo"endFoo#=>NameError:uninitializedconstantFooFoo()#=>"Foo"self.Foo#=>"Foo"为什么会这样?为什么即使在没有同名常量的情况下,也需要明确区分方法调用和对常量的引用? 最佳答案
我正在为我的网站使用MiddlemanBloggem,但默认情况下,博客文章似乎需要位于/source中,这在查看vim中的树时并不是特别好并尝试在其中找到其他文件之一(例如模板)。通过查看文档,我看不出是否有任何方法可以移动博客文章,以便将它们存储在其他地方,例如blog_articles文件夹或类似文件夹。这可能吗? 最佳答案 将以下内容放入您的config.rb文件中。activate:blogdo|blog|blog.permalink=":year-:month-:day-:title.html"blog.sources=
所以我正在关注http://guides.rubyonrails.org/getting_started.html上的官方ROR教程我被困在第5.8节,它教我如何列出所有文章下面是我的controller和index.html.erbControllerclassArticlesControllerindex.html.erbListingarticlesTitleText我收到带有错误消息的NoMethodErrorinArticles#indexundefinedmethod`each'fornil:NilClass"怎么了?我从网站上复制并粘贴了代码以查看我做错了什么,但仍然无法
我收到以下错误:没有路由匹配{:action=>"show",:controller=>"articles",:id=>nil}缺少必需的键:[:id]以下是显示错误的代码。这是什么错误,每当我从上一个屏幕点击编辑时,我想我正在发送文章ID。这是我的rake路由输出PrefixVerbURIPatternController#Actionwelcome_indexGET/welcome/index(.:format)welcome#indexarticlesGET/articles(.:format)articles#indexPOST/articles(.:format)articl
我有一个名为posts的资源,其中有很多。但是,每个帖子可以有多个标签。我希望用户只能从所选标签转到上一篇和下一篇文章。我让它适用于上一个下一个数据库中的所有帖子,但是当我单击一个标签并显示所有标签时,上一个/下一个不符合标签是什么。如果我访问与routes.rb中定义的代码关联的url,get'tags/:tag',to:'posts#index',as::tag,它会列出索引中的所有标签。我不想要这个,我希望用户能够单击上一个或下一个,并且只能在与标签关联的帖子上执行此操作。注意:我使用的是friendly_idgemcontrollers/posts_controller.rbd
我是RubyonRails的新手,我阅读了这些文章。DesignPatternsinRuby:Observer,SingletonDesignPatternsinRuby但我无法理解设计模式和设计原则之间的实际区别。有人可以解释一下区别吗? 最佳答案 设计原则:设计原则是我们在设计软件时应该遵循的核心抽象原则。记住它们不是具体的——而是抽象的。只要我们在允许的条件内,它们就可以以任何语言、任何平台应用,无论处于何种状态。例子:封装变化的内容。针对接口(interface)而非实现编程。依赖抽象。不要依赖于具体的类。设计模式:它们是针
【保姆级】Python最新版开发环境搭建,看这一篇就够了(适用于Python3.11.2安装)文章目录【保姆级】Python最新版开发环境搭建,看这一篇就够了(适用于Python3.11.2安装)一、Python解释器安装Windows安装步骤环境变量配置(非必要)MacOS安装步骤Linux安装步骤二、PyCharm安装三、创建Python工程工欲善其事必先利其器,在使用Python开发程序之前,在计算机上搭建Python开发环境是必不可少的环节,目前Python最新稳定版本是3.11.1,且支持到2027年,如下图所示本文手把手带你从0到1搭建Python最新版3.11.1开发环境,堪称保
1、接口的概念系统与系统之间,组件与组件之间,数据传递交互的通道2、接口的类型按协议划分:http、tcp、IP按语言划分:C++、java、PHP……按范围划分:系统之间多个内部系统之间内部系统与外部系统之间程序之间方法与方法之间、函数与函数之间、模块与模块之间3、接口测试的概念对系统或组件之间的接口进行测试,校验传递的数据正确性和逻辑依赖关系的正确行。4、接口测试的原理主要针对服务器,模拟客户端向服务器发送请求,通过工具或者代码来测试服务器针对客户端请求回发的响应数据是否与预期结果一致。5、接口测试的特点符合质量控制前移的理念可以发现一些页面操作发现不了的问题接口测试低成本高效益接口测试是