用Python学习一下设计模式,如果很枯燥的话,就强行能使用的就用一下。设计模式参考Python与设计模式-途索

1. 单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

import threading
import time class Singleton(object):
'''抽象单例'''
def __new__(self, *args, **kw):
if not hasattr(self,'_instance'):
self._instance = super().__new__(self, *args, **kw)
return self._instance class Bus(Singleton):
'''总线'''
lock = threading.RLock()
def sendData(self, data):
self.lock.acquire()
time.sleep(3)
print("sending signal data... ", data)
self.lock.release() if __name__ == '__main__':
threadList=[]
for i in range(3):
print("entity %d begin to run ... " % i)
t = threading.Thread(target=Bus().sendData,args=("Entity_"+str(i),))
t.start()
threadList.append(t)
for t in threadList:
t.join()

输出:

entity 0 begin to run ...
entity 1 begin to run ...
entity 2 begin to run ...
sending signal data... Entity_0
sending signal data... Entity_1
sending signal data... Entity_2

看到阿里号发在知乎的一篇设计模式下,有人评论python单例模式直接用模块就可以解决。

Python单例模式没必要弄一个单例类,用一个模块就很好,实例直接作为模块内的全局变量

实战

那模拟一下游戏中组团打BOSS的话,BOSS只实例一次,各个角色攻击或者治疗同一个BOSS。或许之后能成woext的文字小游戏。好吧,就正式命名成WOEXT吧。(wow text,致敬今日开的8.0,期待怀旧服)

import time

class Singleton(object):
'''抽象单例'''
def __new__(self, *args, **kw):
if not hasattr(self,'_instance'):
self._instance = super().__new__(self, *args, **kw)
return self._instance class Boss(Singleton):
'''BOSS类'''
def __new__(self):
self.hp=100
self._maxHp=100
return super().__new__(self) def decHp(self,hurt=0):
'''攻击或者治疗'''
if hurt>0:
self.hp-=hurt
self.hp = self.hp if self.hp>0 else 0
elif hurt<0:
self.hp-=hurt
self.hp = self.hp if self.hp<self._maxHp else self._maxHp return self.hp def getHp(self):
return self.hp class Fighter(object):
'''人物类'''
def __init__(self, name, hp=100, attackVal=10):
self.name=name
#self.hp=hp
#self.attackVal=attackVal def attack(self, value=0):
b=Boss()
b.decHp(value)
print("{0}| {1} -> boss, {2} {3} . [Boss hp={4}]".format(time.strftime("%M:%S",time.localtime()), self.name,'伤害' if value>=0 else '治疗',abs(value),b.getHp())) if __name__ == '__main__': mt=Fighter("哀木涕")
mt.attack(10)
lr=Fighter("劣人")
lr.attack(-3)
dz=Fighter("呆呆贼")
dz.attack(12)

Boss类继承单例类,将hp_maxHp属性在__new__中赋值,则拥有了单例属性(唯一、共享)。注:不在super().__new__(self)前赋值的话,则为普通属性。这样还没有赋予抽象单例Singleton太多职责,很单纯的只是实例化。

输出

47:57| 哀木涕 -> boss, 伤害 10 . [Boss hp=90]
47:57| 劣人 -> boss, 治疗 3 . [Boss hp=93]
47:57| 呆呆贼 -> boss, 伤害 12 . [Boss hp=81]

关于Python模块是天然的单例模式。想要实战的话:例子中的打印可以做为战斗日志单独存在,用单例模式(另一个模块文件)实现:

Log.py文件:

class WarLog(object):
'''战斗日志'''
def show(self,message):
print(message) warLog=WarLog()

导入:

from Log import warLog

和使用(将例子attack()方法中print语句改为):

message="{0}| {1} -> boss, {2} {3} . [Boss hp={4}]".format(time.strftime("%M:%S",time.localtime()), self.name,'伤害' if value>=0 else '治疗',abs(value),b.getHp())
warLog.show(message)

p.s. 想要比较两个实例是否为同一个实例,可用is比较: Boss() is Boss()

pss. 无意发现这么单例之后,Boss 类中不能使用带参数的__init__(self,x)了,卡了半天,有的说2.x到3.x这函数__new__(cls)后面参数没有的,和我这个无关,我的和别人错误不一样的原因是还重载了__new__,就没那么简单了,弄得心烦不弄了,反正只是为了单例属性用了__init__赋值之后也没用。搁置。我可只是为了练一下设计模式的啊,还想早早做出文字MT。

psss. 修正上面pss的备注,

Boss类中__new__, 参数不足。def __new__(self, *args, **kw),即可。但也没有用的。因为

class Boss(Singleton):
'''BOSS类'''
def __new__(self, *args, **kw):
self.hp=100
self._maxHp=100
return super().__new__(self)
def __init__(self,x):
self.o_o=x
pass a=Boss('a')
b=Boss('b')
print(a.o_o,b.o_o) #输出 b b

这样的话,后面实例的也会覆盖掉之前的。所以init这种事无所谓的。所以,上面的参数我也不改了,不然我这里说这么多就没有意义了(万一别人也会遇到这种难找的错误呢)。上面这段备注都写得混乱,不好意思。

再补充一句,2.x到3.x继承单例之后如果没有重载__new__的话,有TypeError: object() takes no parameters错误,直接改单例中的__new__(cls)参数即可了。

参考

2. 工厂模式

工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。

简单工厂模式:省去了将工厂实例化的过程

抽象工厂模式:将每一个细致的产品都建立对应的工厂。 (提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类)

简单工厂模式

# 主食
class Burger():
def __init__(self, name='', price=0.0):
self.name=name
self.price=price
self.type='BURGER'
def getPrice(self):
return self.price
def setPrice(self,price):
self.price=price
def getName(self):
return self.name class cheeseBurger(Burger):
def __init__(self):
super().__init__("cheese burger", 10.0) class spicyChickenBurger(Burger):
def __init__(self):
super().__init__("spicy chicken burger", 15.0) # 小吃
class Snack():
def __init__(self, name='', price=0.0):
self.name=name
self.price=price
self.type='SNACK'
def getPrice(self):
return self.price
def setPrice(self, price):
self.price = price
def getName(self):
return self.name class chips(Snack):
def __init__(self):
super().__init__("chips", 6.0) class chickenWings(Snack):
def __init__(self):
super().__init__("chicken wings", 12.0) # 饮料
class Beverage():
def __init__(self, name='', price=0.0):
self.name=name
self.price=price
self.type='BEVERAGE'
def getPrice(self):
return self.price
def setPrice(self, price):
self.price = price
def getName(self):
return self.name class coke(Beverage):
def __init__(self):
super().__init__("coke", 4.0) class milk(Beverage):
def __init__(self):
super().__init__("milk", 5.0) # 工厂
class SimpleFoodFactory():
'''简单工厂模式'''
@classmethod
def createFood(self,foodClass):
print(" factory produce a instance.")
foodIns=foodClass()
return foodIns if __name__=="__main__":
cheese_burger=SimpleFoodFactory.createFood(cheeseBurger)
print(cheese_burger.__dict__)
chicken_wings=SimpleFoodFactory.createFood(chickenWings)
print(chicken_wings.__dict__)
coke_drink=SimpleFoodFactory().createFood(coke)
print(coke_drink.__dict__)

输出:

 factory produce a instance.
{'name': 'cheese burger', 'price': 10.0, 'type': 'BURGER'}
factory produce a instance.
{'name': 'chicken wings', 'price': 12.0, 'type': 'SNACK'}
factory produce a instance.
{'name': 'coke', 'price': 4.0, 'type': 'BEVERAGE'}

抽象工厂模式

# 青蛙与虫子
class Frog(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print('{} the Frog encounters {} and {}!'.format(self, obstacle, obstacle.action())) class Bug(object):
def __str__(self):
return 'a bug'
def action(self):
return 'eats it' class FrogWorld(object):
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Frog World ------'
def make_character(self):
return Frog(self.player_name)
def make_obstacle(self):
return Bug() # 术士与兽人
class Wizard(object):
def __init__(self, name):
self.name = name
def __str__(self):
return self.name
def interact_with(self, obstacle):
print('{} the Wizard battles against {} and {}!'.format(
self, obstacle, obstacle.action())) class Ork(object):
def __str__(self):
return 'an evil ork'
def action(self):
return 'kills it' class WizardWorld(object):
def __init__(self, name):
print(self)
self.player_name = name
def __str__(self):
return '\n\n\t------ Wizard World ------'
def make_character(self):
return Wizard(self.player_name)
def make_obstacle(self):
return Ork() class GameEnvironment(object):
'''游戏入口'''
def __init__(self, factory):
self.hero = factory.make_character()
self.obstacle = factory.make_obstacle()
def play(self):
self.hero.interact_with(self.obstacle) def validate_age(name):
'''年龄检测'''
try:
age = input('Welcome {}. How old are you? '.format(name))
age = int(age)
except ValueError as err:
print("Age {} is invalid, please try again...".format(age))
return (False, age)
return (True, age) if __name__ == '__main__':
name = input("Hello. What's your name? ")
valid_input = False
while not valid_input:
valid_input, age = validate_age(name)
game = FrogWorld if age < 18 else WizardWorld
environment = GameEnvironment(game(name))
environment.play()

FrogWorldWizardWorld为抽象工厂。

通过用户年龄判断执行哪个游戏。

输出

Hello. What's your name? cow
Welcome cow. How old are you? 80
------ Wizard World ------
cow the Wizard battles against an evil ork and kills it!
-----------------------------------------------------------
Hello. What's your name? baa
Welcome baa. How old are you? 12
------ Frog World ------
baa the Frog encounters a bug and eats it!

本来看到这个简书的例子看到也是小游戏的还有点小惊喜,运行以后很糟心。不影响运行的字符串里面的单词错误battlesevil, 影响运行的类名GameEnvironmentFrog。就搞不懂了,现在贴代码非要改一些东西才好防伪么?乱七八糟。再从游戏里面吐槽一下,Bug怎么可以有一个方法是eats it,这是自己的方法,抽象出来也应该像++美味风蛇++一样是eat me啊,不考虑这一层也应该是Frog

有一个killing或者Dining方法而Bugdie或者relish方法。

实战

把工厂模式加到之前的WOEXT中。

其实很虚啊,以前刚看Laravel文档不知是被那晦涩的翻译还是文档组织结构还是不太懂的设计模式虐的惨,所以一个设计模式不敢说都懂,现在只是看到工厂模式可以说这是工厂模式,但是用的时候就得理解到底什么是工厂模式了。这就是看懂和实战的区别,也是实战的意义所在。

一个简单工厂模式

import time
from Log import warLog class Singleton(object):
'''抽象单例'''
def __new__(self, *args, **kw):
if not hasattr(self,'_instance'):
self._instance = super().__new__(self, *args, **kw)
return self._instance class Boss(Singleton):
'''BOSS类'''
def __new__(self, *args, **kw):
self.hp=100
self._maxHp=100
return super().__new__(self) def decHp(self,hurt=0):
'''攻击或者治疗'''
if hurt>0:
self.hp-=hurt
self.hp = self.hp if self.hp>0 else 0
elif hurt<0:
self.hp-=hurt
self.hp = self.hp if self.hp<self._maxHp else self._maxHp return self.hp def getHp(self):
return self.hp class Fighter(object):
'''抽象人物类'''
def __init__(self, name, hp=100, attackVal=10):
self.name=name
self.hp=hp
self.attackVal=attackVal def attack(self):
value=self.attackVal b=Boss()
b.decHp(value)
message="{0}| {1} -> boss, {2} {3} . [Boss hp={4}]".format(time.strftime("%M:%S",time.localtime()), self.name,'伤害' if value>=0 else '治疗',abs(value),b.getHp())
warLog.show(message) class Warrior(Fighter):
'''战士'''
name='战士'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 150, 10) class Hunter(Fighter):
'''猎人'''
name='猎人'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 100, 8) class Rogue(Fighter):
'''盗贼'''
name='盗贼'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 80, 12) class WarFactory(object):
'''战争工厂'''
@classmethod
def createFighter(self, fighter, name=''):
return fighter(name) if __name__ == '__main__': zs=WarFactory.createFighter(Warrior, '哀木涕')
zs.attack()
lr=WarFactory.createFighter(Hunter, '劣人')
lr.attack()
dz=WarFactory.createFighter(Rogue, '呆呆贼')
dz.attack()

输出

09:25| 哀木涕 -> boss, 伤害 10 . [Boss hp=90]
09:25| 劣人 -> boss, 伤害 8 . [Boss hp=82]
09:25| 呆呆贼 -> boss, 伤害 12 . [Boss hp=70]

其中,Fighter为抽象的工厂类,Warrior,Hunter,Rogue为具体的工厂类。不过貌似还没有直接创建人物简单呢,那就再补一个场景:需要人海战术打Boss,每个角色只能补一刀。那么调用只需改成:

while Boss().getHp()>0:
r=random.choice([Warrior, Hunter, Rogue])
WarFactory.createFighter(r).attack()

输出:

17:42| 战士 -> boss, 伤害 10 . [Boss hp=90]
17:42| 盗贼 -> boss, 伤害 12 . [Boss hp=78]
17:42| 战士 -> boss, 伤害 10 . [Boss hp=68]
17:42| 猎人 -> boss, 伤害 8 . [Boss hp=60]
17:42| 战士 -> boss, 伤害 10 . [Boss hp=50]
17:42| 猎人 -> boss, 伤害 8 . [Boss hp=42]
17:42| 战士 -> boss, 伤害 10 . [Boss hp=32]
17:42| 盗贼 -> boss, 伤害 12 . [Boss hp=20]
17:42| 猎人 -> boss, 伤害 8 . [Boss hp=12]
17:42| 盗贼 -> boss, 伤害 12 . [Boss hp=0]

当然现在Boss无还手之力,之后加上治疗之类的就会更有意思的。(巴特,我还是不知道自己有没有理解工厂

参考

3. 建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

这个比较好理解啊,就是在一个类中,把需要的模块组合好。真·建造。就不放照抄的代码了,直接实例。

把萨满也加上,让他们四个角色组成一个菊爆小队。

import time
import random
from Log import warLog class Singleton(object):
'''抽象单例'''
def __new__(self, *args, **kw):
if not hasattr(self,'_instance'):
self._instance = super().__new__(self, *args, **kw)
return self._instance class Boss(Singleton):
'''BOSS类'''
def __new__(self, *args, **kw):
self.hp=100
self._maxHp=100
return super().__new__(self) def decHp(self,hurt=0):
'''攻击或者治疗'''
if hurt>0:
self.hp-=hurt
self.hp = self.hp if self.hp>0 else 0
elif hurt<0:
self.hp-=hurt
self.hp = self.hp if self.hp<self._maxHp else self._maxHp return self.hp def getHp(self):
return self.hp class Fighter(object):
'''抽象人物类'''
def __init__(self, name, hp=100, attackVal=10):
self.name=name
self.hp=hp
self.attackVal=attackVal def attack(self):
value=self.attackVal b=Boss()
b.decHp(value)
message="{0} -> boss, {1} {2} . [Boss hp={3}]".format( self.name,'伤害' if value>=0 else '治疗',abs(value),b.getHp())
warLog.show(message) class Warrior(Fighter):
'''战士'''
name='战士'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 150, 10) class Hunter(Fighter):
'''猎人'''
name='猎人'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 100, 8) class Rogue(Fighter):
'''盗贼'''
name='盗贼'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 80, 12) class Shaman(Fighter):
'''萨满'''
name='萨满'
def __init__(self, name=""):
if name: self.name=str(name)
super().__init__(self.name, 80, 6) class WarFactory(object):
'''战争工厂'''
@classmethod
def createFighter(self, fighter, name=''):
return fighter(name) # 建造模式,用来组队 class TeamUp(object):
'''组队(生产)'''
def __init__(self, teamName="小队"):
self.teamName=teamName
self.total=4
self.zs=None
self.ls=None
self.dz=None
self.sm=None
self.team=[]
warLog.show("来人啊,我们{0}要组队了".format(self.teamName)) def enrollment(self, fighter):
'''入队'''
self.team.append(WarFactory.createFighter(fighter))
warLog.show("{0} 入队一人:{1}".format(self.teamName,fighter.name)) def __repr__(self):
if len(self.team)==self.total:
return "{0}成员:{1}, {2}, {3} 以及 {4}。".format(self.teamName,*[a.name for a in self.team])
return "小组不足{0}人".format(self.total) class TeamDirector(object):
'''组队过程(步骤)'''
def __init__(self, builder):
self.group = builder def createTeam(self, zs, lr, dz, sm):
'''入队人员'''
self.group.enrollment(zs)
self.group.enrollment(lr)
self.group.enrollment(dz)
self.group.enrollment(sm) def show(self):
warLog.show(self.group) if __name__ == '__main__':
# 建造者模式 菊爆小队
team=TeamDirector(TeamUp('【菊爆小队】'))
team.createTeam(Warrior, Hunter, Rogue, Shaman)
team.show()

输出:

19:15:49 来人啊,我们【菊爆小队】要组队了
19:15:49 【菊爆小队】 入队一人:战士
19:15:49 【菊爆小队】 入队一人:猎人
19:15:49 【菊爆小队】 入队一人:盗贼
19:15:49 【菊爆小队】 入队一人:萨满
19:15:49 【菊爆小队】成员:战士, 猎人, 盗贼 以及 萨满。

用到了上节的工厂特性。

4. 原型模式

用原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象

其实最简单的理解还是Python原型模式这一篇

如果想根据现有对象复制出新的对象并对其修改,可以考虑原型模式

多简单,好娇柔和不造作,和那些妖艳贱货完全不一样。其他文章总是把深浅拷贝拿出来做重点说,其实从原型模式角度只是复制一份对象而已啊,深浅拷贝是其他重点了(我觉得)。修改一下来一个简单的原型模式:

#原型模式
import copy class Point:
def __init__(self, x, y, l):
print(l)
self.x = x
self.y = y
self.l = l p1=Point(1,2,[1,2,3])
p2=copy.copy(p1) # 浅拷贝
p3=copy.deepcopy(p1) # 深拷贝 p1.x=0 # 修改 不可改变对象
p1.l.append(4) # 修改(列表)可改变对象 (浅拷贝只会复制原列表的引用,指向同一个对象,所以修改时会更改p2.l) print(p1, p1.__dict__)
print(p2, p2.__dict__)
print(p3, p3.__dict__)

输出:

[1, 2, 3] #可以看到只执行了一次初始化
<__main__.Point object at 0x03A7AD10> {'x': 0, 'y': 2, 'l': [1, 2, 3, 4]}
<__main__.Point object at 0x03632490> {'x': 1, 'y': 2, 'l': [1, 2, 3, 4]}
<__main__.Point object at 0x03A8AB50> {'x': 1, 'y': 2, 'l': [1, 2, 3]}

这里就没有实例了,太简单了

Python与设计模式之创建型模式及实战的更多相关文章

  1. Python版设计模式: 创建型模式:单例模式和工厂模式家族

    一. 单例模式(Singleton) 所谓单例模式,也就是说不管什么时候都要确保只有一个对象实例存在.很多情况下,整个系统中只需要存在一个对象,所有的信息都从这个对象获取,比如系统的配置对象,或者是线 ...

  2. Java设计模式之创建型模式

    创建型模式分为五类:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式 一.工厂方法模式:接口-实现类.工厂类

  3. GoF的23种设计模式之创建型模式的特点和分类

    创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是“将对象的创建与使用分离”.这样可以降低系统的耦合度,使用者不需要关注对象的创建细节,对象的创建由相关的工厂来完成.就像我们去商场购买商品时, ...

  4. Typescript玩转设计模式 之 创建型模式

    作者简介 joey 蚂蚁金服·数据体验技术团队 前言 我们团队的工作是用单页面应用的方式实现web工具.涉及到数万到十数万行的前端代码的管理,而且项目周期长达数年. 怎么样很好地管理好这种量级的前端代 ...

  5. 设计模式01 创建型模式 - 单例模式(Singleton Pattern)

    参考 [1] 设计模式之:创建型设计模式(6种) | 博客园 [2] 单例模式的八种写法比较 | 博客园 单例模式(Singleton  Pattern) 确保一个类有且仅有一个实例,并且为客户提供一 ...

  6. (转自精通Python设计模式)Python设计模式之创建型模式——2.建造者模式

    建造者模式将一个复杂对象的构造过程与其表现分离,这样,同一个构造过程可用于创建多个不同的表现. 我们来看个实际的例子,假设我们想要创建一个HMTL页面生成器,HTML页面的基本结构(构造组件)通常是一 ...

  7. python设计模式1:创建型模式

    1.原型模式 如果想根据现有的对象复制出新的对象并进行修改,可以考虑“原型模式”,而无需知道任何创建细节.(有点像写轮眼...你不需要知道它) import copy class Point: __s ...

  8. [C#]设计模式-单例模式-创建型模式

    单例模式用于在整个软件系统当中保持唯一实例,在 C# 当中最能够体现此概念的就是静态类,静态类的生命周期是跟随整个程序,并且在整个程序中仅保有一个实例. 不过在这里我们不再详细阐述单例模式与静态类有什 ...

  9. 设计模式01 创建型模式 - 原型模式(Protype Pattern)

    参考 1. 设计模式:原型模式 | 博客园 2. Java clone深拷贝.浅拷贝 | CSDN 3. Cloneable接口和Object的clone()方法 | 博客园 原型模式(Prototy ...

随机推荐

  1. JVM配置参数详解

    记录一下jvm中的一些配置参数,这些肯定不全的,希望你们能留个言,补全一下,谢谢啦! -XX: MaxDirectMemorySize--->设置直接内存,不设置与Java堆内存最大值一致 -X ...

  2. js splice函数 数组增加,替换,删除

    splice函数参数介绍: 第一个参数: 对于数数组的操作起始位置. 第二个参数: 从第一个参数开始,删除数组中的个数. 从第三个参数之后所有参数(如果有):执行完第二步之后,都插入到第一个参数的起始 ...

  3. 精确值避免使用float和double,使用BigDecimal

    实现将double类型的值转换为BigDecimal类型的值的不同途径以及各途径间的区别 一:有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal  ...

  4. 2017-9-3模拟赛T1 卡片(card)

    题目 [题目描述] lrb 喜欢玩卡牌.他手上现在有n张牌,每张牌的颜色为红绿蓝中的一种.现在他有两种操作.一是可以将两张任意位置的不同色的牌换成一张第三种颜色的牌:二是可以将任意位置的两张相同颜色的 ...

  5. IC卡_状态码SW1、SW2

    9000 正常 成功执行 61xx 正常 需要发GET RESPONSE命令 6200 警告 信息未提供 6281 警告 回送数据可能出错 6282 警告 文件长度小于Le 6283 警告 选中的文件 ...

  6. IDEA 类图功能使用方法

    1. Ctrl+Shift+Alt+U显示类图,(可以选中代码中类,再按快捷键,直接进入此类的类图) 2. 在类图中,选中某类右击显示Show Implementations,弹出子类的选择框. 按S ...

  7. CIFAR-10数据集读取

    参考:https://jingyan.baidu.com/article/656db9183296c7e381249cf4.html 1.使用读取方式pickle def unpickle(file) ...

  8. C# EasyHook MessageBox 示例(极简而全)

    完整代码,原创无藏私,绝对实用.Windows10 X64 下调试通过,对 w3wp.exe, sqlserver.exe,notepad.exe,iexporer.exe 注入后,长时间运行稳定,未 ...

  9. IntelliJ IDEA 新版发布:支持CPU火焰图,新增酷炫主题

    JetBrain 是一家伟大的公司,一直致力于为开发者开发世界上最好用的集成开发环境 就在上周,JetBrain 公司发布了 Java 集成开发环境 IntelliJ IDEA 最新版本 2018.3 ...

  10. [visual studio]visual studio 2017激活码

    企业版:NJVYC-BMHX2-G77MM-4XJMR-6Q8QF 专业版:KBJFW-NXHK6-W4WJM-CRMQB-G3CDH