• Streamline Your App with Design Patterns 用设计模式精简你的应用程序


    Streamline Your App with Design Patterns

    用设计模式精简你的应用程序

    In Objective-C programming, one way to add behavior specific to your app is through inheritance. You create a subclass of an existing class that either augments the attributes and behavior of the superclass or modifies them in some way. But there are other, more dynamic ways of adding app-specific behavior that do not involve subclassing. These dynamic techniques and approaches are based on design patterns. As this articles explains, adopting design patterns in your code contributes to the reusability and extensibility of both your classes and the framework classes.

    在Objective-C 编程中,一种往你的应用程序添加特定行为的方法是通过继承。 你创建一个已经存在的类的子类,继承相同的参数属性和行为或以相同的方式修改它们。 但是这里有另外一种不需要子类化,并且更动态化的方法。 这些动态科技和方法都基于设计模式。正如这篇文章所述,在你的代码里采用设计模式可以提高你和框架的可重用性和可扩展性。

    Design Pattern: A Template for a Design That Solves a Programming Problem

    设计模式:设计模板解决编程问题

    A design pattern is a tool of abstraction used in object-oriented software development as well as other fields. It is a template for a design that solves a general, recurring problem in a particular context. A design pattern is thus a kind of guide for a particular, concrete design: an “instantiation” of a pattern, in a sense. There is some flexibility in how you can apply a design pattern, and often things such as the programming language and existing architectures can determine how the pattern is applied. 

    设计模式是一种在面向对象软件开发以及别的很多领域中使用的抽象工具。它是一个设计模板,用来解决在一个特殊环境(context)中普遍的,经常发生的问题。因此设计模式是一种特殊的,具体的设计指南:在一定意义上是一种模式的一个实例化("instantiation")。 你可以如何实施一个设计模式有一些灵活性,并且像编程语言和存在的架构(architectures)等能决定如何实施设计模式。

    Several themes or principles of design influence design patterns. These design principles are rules of thumb for constructing object-oriented systems, such as “encapsulate the aspects of system structure that vary” and “program to an interface, not an implementation.” They express important insights. For example, the principle of encapsulation says that if you isolate the parts of a system that vary and encapsulate them, they can vary independently of other parts of the system, especially if you define interfaces for them that are not tied to implementation specifics. You can later alter or extend those variable parts without affecting the other parts of the system. You thus eliminate dependencies and reduce couplings between parts, and consequently the system becomes more flexible and easier to change.

    一些主题或设计原则影响设计模式。 这些设计原则是构建面向对象系统的经验法则,比如"封装变化的系统结构方面" 和 "面向接口编程,而非面向实现"。它们表达了重要见解。比如,封装原则告诉我们如果你把系统变化部分分离出来并封装它们,那么它们就能独立于系统的其它部分发生变化,特别是如果你为它们定义了不依赖于实现细节的接口。 你可以在稍候更改或扩展那些变化部分而不会影响系统的其它部分。 从而消除依赖,减少部件之间的关联, 因此系统变得更加灵活,更容易被改变。

    Benefits such as these make design patterns an important consideration when you’re writing software. If you find, adapt, and use patterns in your app’s design, that app—and the objects and classes that it comprises—will be more reusable, more extensible, and easier to change when future requirements demand it. Moreover, apps that are based on design patterns are generally more elegant and efficient than programs that aren’t, because they require fewer lines of code to accomplish the same goal.

    诸如这样的优点让设计模式在软件编写时变成一项很重要的考虑因素。 如果你在设计应用时找到模式,调整(adapt)并使用它们, 它们能提高该应用--构成的对象和类--的可重用性,可扩展性以及在将来需求变化时更容易更改。 还有,那些基于设计模式的应用程序比没有设计模式的应用程序更加优雅和高效,因为它们要求更少的代码行就能完成相同的目标。

    You can find adaptations of design patterns throughout the Cocoa and Cocoa Touch frameworks and in the Objective-C runtime and language. Some of these pattern-based mechanisms you get almost “for free,” but others require some work on your part. And you can apply design patterns to your own app’s code when the situation warrants it. If you use the same patterns that the frameworks use, your code tends to fit in better with the frameworks’ code and work more elegantly.

    您会发现,设计模式的应用贯穿于整个 Cocoa Touch 和 Cocoa 框架、Objective-C 的运行时及程序设计语言自身。你几乎可以免费获得一些基于模式的机制,但是其它机制则需要你做一些工作。 必要时你可以在你自己的应用程序代码里实施设计模式。 如果你跟框架使用相同的设计模式,你的代码将能更好的跟框架代码相适应,并能工作的更优雅。

    The Most Important Design Pattern: Model-View-Controller

    最重要的设计模式:模型-视图-控制器

    The Model-View-Controller design pattern (commonly known as MVC) assigns objects in an app one of three roles: model, view, or controller. The pattern defines not only the roles objects play in the app, it defines the way objects communicate with each other. Each of the three types of objects is separated from the others by abstract boundaries and communicates with objects of the other types across those boundaries. The collection of objects of a certain MVC type in an app is sometimes referred to as a layer—for example, a model layer.

    模型-视图-控制器(通常被称为MVC)把应用程序里的所有对象分为3类:模型,视图 和 控制器。 设计模式不仅仅定义应用程序里所有对象的角色,还定义了各个对象之间的通信方式。 每类对象都通过抽象边界跟其它类对象相分离, 并跨越那些边界跟别类对象相互通信。应用程序中一个特定MVC类型的所有对象集有时简称为层---比如,一个模型层。

    image: ../Art/model_view_controller_2x.png

    MVC is central to a good design for any app. The benefits of adopting this pattern are numerous. Many objects in these apps tend to be more reusable, and their interfaces tend to be better defined. Apps having an MVC design are also more easily extensible than other apps. Moreover, many technologies and architectures that your app can use are based on MVC and require that your custom objects play one of the MVC roles.

    对于任何应用程序,MVC设计模式是一个好设计的核心。 采用这个设计模式的好处不胜枚举。 这些应用程序中的很多对象都趋向于更加可重用,它们的接口趋向于定义的更好。 拥有一个MVC设计模式的应用程序也比其它应用程序更加容易被扩展。 此外,很多你的应用程序能用的技术和架构都是基于MVC设计模式,并要求你自定义的对象扮演其中一个MVC角色。

    You may not be aware of it but you have already created an app that is based on MVC: TrackMix (in Your First Mac App). The instance of the Track class is a model object; the AppDelegate object is the controller object; and the window and all the views it contains are the view layer of the app.

    你可以没有意识到,但是你确实已经创建了一个基于MVC模式的应用程序:TrackMix(在Your First Mac App教程)。Track类的实例是一个模型对象;AppDelegate对象是控制器对象;窗口以及所有它包含的视图属于应用程序的视图层。

    Complete information about Model-View-Controller is in “Model-View-Controller” in Concepts in Objective-C Programming.

    有关 模型-视图-控制器 设计模式的完整信息,请参阅《Concepts in Objective-C Programming》(Objective-C 编程中的概念)中的“Model-View-Controller”。

    Model Object

    A model object encapsulates the data of an app and defines the logic and computation that manipulate and process that data. For example, a model object might represent a character in a game or a contact in an address book. Sometimes the model layer of an app is effectively one or more graphs of related objects. Much of the data that is part of the persistent state of the app (whether that persistent state is stored in files or databases) should reside in the model objects after the data is loaded into the app. Because model objects represent knowledge and expertise related to a specific problem domain, they can be reused in similar problem domains. A “pure” model object should have no explicit connection to the view objects that present its data and allow users to edit that data—it should not be concerned with user-interface and presentation issues.

    一个模型对象封装了一个应用程序的数据,并定义了操作和处理那些数据的逻辑和运算。比如,一个模型对象可能在一个游戏角色或一个地址簿的联系人。有时模型层里包含一个或多个相关的对象。 当数据被载入到应用程序之后,部分应用程序持久化状态(不管这持久化状态是存在文件中还是数据库中)应该保留在模型对象中。因为模型对象代表跟一个特定问题域相关的知识和专长,它们在相似的问题领域里能被重复使用。 一个"pure-纯"模型对象应该不跟视图对象有明确的连接(视图显示数据,并允许用户编辑那些数据),它不应该关注用户界面和外观问题。

    User actions in the view layer that create or modify data are communicated through a controller object and result in the creation or updating of a model object. When a model object changes (for example, new data is received over a network connection), it notifies a controller object, which updates the appropriate view objects.

    视图层上创建或修改数据的用户操作通过一个控制器对象进行通信,并返回一个模型对象的创建和更新。 当模型对象发生改变时(比如,从网络连接那接收到新数据),它通知控制器对象,控制器对象更新相应的视图对象。

    View Object

    A view object is an object in an app that users can see. A view object knows how to draw itself and might respond to user actions. A major purpose of view objects is to display data from the app’s model objects and to enable the editing of that data. Despite this, in an MVC app view objects are typically decoupled from model objects.

    视图对象是应用程序里用户能够看到的对象。 视图对象知道如何绘制自己,并可能对用户操作做出反应。 视图对象的一个主要目的就是显示应用程序模型对象的数据,并允许用户编辑那些数据。 尽管如此,在MVC应用程序里,视图对象通常跟模型对象分离。

    Because you typically reuse view objects and reconfigure them, view objects provide consistency between apps. For OS X, the AppKit framework provides collections of view classes. In AppKit, a view object ultimately inherits from theNSView class.

    因为你通常重复使用和重新配置视图对象,视图对象在应用程序之间提供一致性。 对于OS X, AppKit 框架提供了各种视图类集。 在AppKit框架中,视图对象最终继承自NSView 类。

    View objects learn about changes in model data through the app’s controller objects and communicate user-initiated changes—for example, text entered in a text field—through controller objects to an app’s model objects.

    视图对象通过应用程序控制器对象获取模型数据的改变, 并通过控制器对象,将用户发动的改变--比如,在文本框输入文本,发送到应用程序模型对象。

    Controller Object

    A controller object acts as an intermediary between one or more of an app’s view objects and one or more of its model objects. Controller objects are thus a conduit through which view objects learn about changes in model objects and vice versa. Controller objects can also perform setup and coordinating tasks for an app and manage the life cycles of other objects.

    控制器对象在 一个或多个应用程序视图对象和 一个或多个模型对象之间扮演着中间人的角色。 因此,控制器对象是视图对象了解模型对象变化的一个管道,反之亦然。 控制器对象也能执行应用程序的启动和协调任务,它们还能控制其它对象的生命周期。

    A controller object interprets user actions made in view objects and communicates new or changed data to the model layer. When model objects change, a controller object communicates that new model data to the view objects so that they can display it.

    控制器对象解释视图对象中的用户操作,并把新的或发生改变的数据发送给模型层。当模型对象发生改变时,控制器给视图对象发送新的模型数据,从而显示数据。

    Solving Problems with Design Patterns

    用设计模型解决问题

    An object-oriented system such as an app is dynamic. What an object can do at runtime is not limited to behavior set at the time of compilation. An object can send messages to other objects, and the target of the same message can vary by runtime circumstance. An object can also cooperate with a variable group of other objects at runtime, using a variety of techniques, to efficiently accomplish the work of the app. For an object or a network of objects to do this, it must take advantage of the many techniques and framework architectures that are adaptations of design patterns.

    面向对象系统,比如应用程序,是动态的。 对象在runtime能做的不仅限于在编译时设置的行为。 对象能给别的对象发送消息, 同一个消息的目标在runtime周期内也可以发生变化。 对象还能在runtime使用各种各样的技术跟其它对象的变化组合作,有效的完成应用程序的工作。 如果一个对象或一个对象网络想这样做,它必须掌握很多技术以及适用于设计模式的框架架构。

    The following sections describe many of these techniques and architectures. Consider them part of your Objective-C programming toolkit.

    接下来的章节描述了很多这样的技术和架构。 请将它们作为您 Objective-C 编程工具箱的一部分。

    Delegation: Acting on Behalf of Another Object

    委托/代理:代表另一个对象

    In delegation, an object called the delegate acts on behalf of, and at the request of, another object. That other, delegating, object is typically a framework object. At some point in execution, it sends a message to its delegate; the message tells the delegate that some event is about to happen and asks for some response. The delegate (usually an instance of a custom class) implements the method invoked by the message and returns an appropriate value. Often that value is a Boolean value that tells the delegating object whether to proceed with an action.

    在委托中,一个叫做delegate的对象应另一个对象要求,作为该对象的代表。另外,委托对象通常是一个框架对象。在执行时的某些点,它给它的委托发送消息; 该信息告诉委托一些事件将要发生,并请求一些相应。 委托(常常是一个自定义类的实例)实现消息调用的方法,返回相应值。通常该返回值是一个布尔值,用来告诉委托对象是否继续操作。

    image: ../Art/delegation_2x.png

    Delegation is thus a means for injecting application-specific behavior into the workings of a framework class—without having to subclass that class. It is a common and powerful design for extending and influencing the behavior of the frameworks.

    因此委托是往框架类工作加入应用程序特定行为的一种手段--而不需要成为那个类的子类。这是扩展和影响框架行为的一个通用并强大的设计。

    Recall the AppDelegate object in the TracMix app you created when working through Your First Mac App. Xcode automatically assigns it to be the delegate of the app object. The app delegate handles theapplicationDidFinishLaunching: message sent to it by the app.

    回想你在TrackMix应用程序中的AppDelegate 对象,在Your First Mac App教程里创建。 Xcode自动把它分配为应用程序对象的委托。 应用程序委托处理应用程序发送给它的 applicationDidFinishLaunching:消息。

    There are two programmatic components to delegation. The delegating class must define a property (by convention named delegate) to hold a reference to the delegate. It must also declare a protocol that the class of the delegate must adopt (see the following section for more on protocols). Many classes of the frameworks offer delegation as an approach that apps can take to augment framework behavior with something that is specific to the app.

    委托有2个可编程组件。 委托类必须定义一个特性(property)--根据约定命名为delegate---用来保存一个指向委托的引用。它还必须声明一个委托类必须采用的协议(查看以下章节获取更多有关协议的知识)。 很多框架类提供委托作为一种方法,用来让应用程序增加应用程序特定的框架行为。

    Delegation, however, is not limited to framework classes. You can implement delegation between two custom objects in an app.

    然而,委托不仅限于框架类。 你可以在应用程序里实现两个自定义对象之间的委托。

    Protocol: Enabling Communication Between Objects Not Related by Inheritance

    协议:让毫无相关的对象之间能够通过继承发生通信

    A protocol is a declaration of a programmatic interface whose methods any class can implement. An instance of a class associated with the protocol calls the methods of the protocol and obtains values returned by the class formallyadopting and implementing the protocol. This communication between the objects furthers a specific goal, such as parsing XML code or copying an object. The objects on either side of the protocol interface can be distantly related to each other by inheritance. A protocol is thus, as is delegation, an alternative to subclassing and is often part of a framework’s implementation of delegation.

    协议是一个可编程接口的声明,任何类都能实现该接口的方法。 跟协议相关的一个类的实例调用这些协议方法,并获得正式采用和实现该协议的类的返回值。对象之间的此类通信产生了一个特定目标,比如解析XML代码 或 拷贝一个对象。在协议接口两边的对象能通过继承远远地相互关联。因此协议跟委托一样,可以作为子类化的替换手段,通常是框架实现委托的一部分。

    image: ../Art/protocol.png

    The frameworks that Apple provides declare dozens of protocols. In addition, your app can declare custom protocols that your classes can adopt. Protocols are part of your programming toolkit.

    Apple公司提供的框架里声明了数十个协议。 另外,你的应用程序能声明自定义协议,你的类能采用它们。 协议是你变成工具箱的一部分。

    Notification Center: Notifying Interested Observers of an Event

    通知中心:通知对事件感兴趣的观察者

    A notification center is a subsystem of the Foundation framework that broadcasts a message—a notification—to all objects in an app that are registered observers of an event. (Programmatically, it’s an instance of theNSNotificationCenter class.) The event can be anything that happens in an app—the app entering the background state, for instance, or the user starting to type in a text field. A notification informs the observer that the event has happened or is about to happen, thus giving the observer an opportunity to respond in an appropriate manner. Notifications broadcast by the notification center are a way to increase cooperation and cohesion among the objects of an app.

    通知中心是Foundation 框架的一个子系统,它在应用里向所有对象发送一个消息---一个通知---,这就是注册一个事件的观察者。(从编程上讲,它就是NSNotificationCenter类的一个实例) 事件可以是应用程序里发生的任何事情--比如,应用程序进入后台状态,或用户开始往文本框输入。 通知(notification)告诉观察者事件已经发生或将要发生,因此让观察者有机会以适当的方式对事件做出反应。通过通知中心来传播通知,是增加应用程序对象间合作和内聚力的一种途径。

    image: ../Art/notificationcenter_2x.png

    For example, a controller object in a Mac app can observe the notificationNSColorPanelColorDidChangeNotification to find out how the color selected by the user for a particular task. As this example indicates, a notification is an object that has a name indicating a specific event and whether that event has occurred or is about to occur. It also carries a reference to the object that posts (or sends) the notification to the notification center, and it can contain a dictionary of supplementary information.

    举个例子,Mac 应用程序里的控制器对象能观察 NSColorPanelColorDidChangeNotification通知,用来找出用户在一个特殊任务如何选择颜色。正如这个例子所述,通知是一个对象,它的名字用来说明一个特殊事件,是否已经发生或正要发生。 它同时载有一个指向对象的引用,用来发布(或发送)通知到通知中心, 它能包含一个有补充信息的字典。

    Any object can observe a notification, but to do so it must register to receive it. In registering, it must specify a selector identifying the method to be invoked by the delivery of the notification; the method signature must have a single parameter: the notification object. When registering, the observer can also specify the posting object.

    任何对象都能观察一个通知,但是你必须注册该对象以便接收通知。 注册时,必须指定一个选择器,用来辨认通知传递时被调用的方法;方法前面必须有只有一个参数:通知对象(notification object)。当注册时,观察者也能指定发布对象(posting object)。

    Notification-center notifications are similar to delegation messages; both are sent to arbitrary objects when certain events occur. However, methods that handle notifications, unlike delegation methods, cannot return a value. And notification via the notification center is synchronous, just as with delegation.

    通知中心的通知跟委托消息很相似;它们都是在特定事件发生时发送给任意对象。然而,处理通知的方法跟处理委托消息的方法不一样,它没有返回值。通过通知中心的通知是同步的,跟委托一样。

    The AppKit framework defines many delegation methods with no return values that correspond to notifications. If a delegate implements such a method, it is handling the notification. For example, if a delegate implements thewindowDidMove: method, it is handling the notification NSWindowDidMoveNotification.

    AppKit 框架定义了很多没有返回值的委托方法,这些方法都跟通知想关联。 如果一个委托实现了这样方法,则它能处理通知。比如,如果一个委托实现了 windowDidMove: 方法,它就能处理 NSWindowDidMoveNotification通知。

    A custom object in your app can define and post its own notifications, and other custom objects in your app can observe the notification.

    应用程序里的自定义对象能定义和发布自己的通知,而应用里的其它自定义对象能观察这些通知。

    Target-Action: Encapsulating a Message to Be Sent When an Event Occurs

    目标-操作:事件发生时封装要发送的消息

    The target-action design is conceptually simple. An object stores the elements that make up a message expression and, when a certain event occurs, puts these elements together and sends a message. The elements are a selector identifying the message (the action) and the object to receive the message (the target). The target’s class implements the method corresponding to the action and the target, and when it receives the message at runtime, responds to the event by executing the method.

    概念上 目标-操作 设计很简单。 一个对象存储着组成消息表达式的各个元素,当一个特定事件发生时,把这些元素收集到一起,并发送一个消息。这些元素是辨别消息(操作)的一个选择器,该对象则是接收该消息(操作). 目标类实现跟操作和目标相关的方法,当它在runtime接收到消息时,执行该方法对事件做出响应。

    Target-action is primarily a feature of controls in both the Cocoa and Cocoa Touch frameworks. A control is a user-interface object such as a button, slider, or switch that users manipulate (by clicking, tapping, dragging, and so on) to signal their intention to an app. Most Cocoa controls are paired with one or more cell objects that store target and action; a Cocoa Touch control, on the other hand, stores both action and target.

    目标-操作 主要是Cocoa 和 Cocoa Touch 框架里控制器的一个特性。控制器是一个用户接口对象,比如用户操纵按钮,滑动条或开关(通过点击,敲击,拖拉等等)给应用程序发送信号。 大多数Cocoa控制器跟一个或多个单元对象想配对,这些单元对象存储了目标和操作;而Cocoa Touch 控制器则同时存储操作和目标。

    image: ../Art/target_action_OSX.png

    Key-Value Observing: Notifying an Observer When a Value Changes

    键-值 观察:值发生变化时通知观察者

    Key-value observing (or KVO) allows an object to observe a property of another object. The observing object is notified when that property’s value changes. It learns about the old value as well as the new one; if the observed property is a to-many relationship (for example, an array), it also learns which of the contained objects are involved in the change. KVO helps apps to become more cohesive by keeping objects in the model, controller, and view layers synchronized to changes.

    键值观察(KVO)允许一个对象观察另一个对象的属性。当那个属性的值发生变化时,观察对象获得通知。 它了解新值以及旧值;如果被观察的属性是一对多关系(比如,一个数组), 它同时也了解哪个被包含的对象发生了改变。 KVO 帮助应用程序变得更加有凝聚力,它通过保证模型,控制器以及视图层上的对象能跟变化同步。

    image: ../Art/kvo.png

    Similar to NSNotificationCenter notifications, multiple KVO observers can observe a single property. Moreover, KVO is more dynamic because it allows objects to observe arbitrary properties without requiring any new API, such as a notification name. KVO is a lightweight point-to-point communication mechanism that doesn’t allow observing a specific property of all instances.

    跟NSNotificationCenter 通知很像, 多个KVO观察者可以观察同一个属性。 进一步说,KVO比NSNotificationCenter更加动态,因为它允许对象观察任意的属性而不需要任何新API, 比如通知名。 KVO 是一个轻量级点对点通信机制,它不允许观察所有实例的特定属性。

    Other Framework Designs Based on Design Patterns

    基于设计模式的其它框架设计

    The Cocoa and Cocoa Touch frameworks also include other designs based on design patterns, including the following:

    Cocoa  和 Cocoa Touch 框架还包含了基于设计模式的其它设计,如下:

    • View hierarchy. The views an app presents are arranged in a hierarchical organization based visually on containment. This pattern allows apps to treat individual views and compositions of views uniformly. At the root of the hierarchy is a window object; each view under that root has one parent view and zero or more child views. Parent views enclose child views. The view hierarchy is a structural component of both drawing and event handling.

      视图层次。 在应用程序里出现的视图都被排列成一个层次组织,该组织视觉上基于包含(containment)。 这个模式允许应用程序同等对待单个视图和合成视图。 在层次的根节点是一个窗口对象;每个根节点有一个父视图,以及0个或多个子视图。 父视图包含子视图。 视图曾是是绘图和事件处理的一个结构性组件。

    • Responder chain. The responder chain is a series of objects—mostly views, but also windows, view controllers, and the application object itself—along which an event or action message can be passed until one object in the chain handles the event. It thus is a mechanism for cooperative event handling. The responder chain is closely related to the view hierarchy.

      响应器链。响应器链是一系列的对象---几乎都是视图,但是也包含窗口,视图控制器以及应用程序对象本身---事件或操作消息可以沿着该响应器链被传递直到链中的某个对象处理该事件。

    • Receptionist. In the Receptionist pattern, work that an app is performing is redirected—or “bounced”—from one execution context to another. (An execution context is a dispatch queue or operation queue associated with the main thread or a secondary thread.) You apply the Receptionist pattern primarily when work performed in a secondary queue results in a task that must must be performed on the main queue—for example, in operations updating the user interface.

      前台。 在前台模式中,应用程序所执行的工作,是从一个执行环境重定向(弹回)到另一个。(一个执行环境是跟主线程或辅助线程相关联的一个分发队列或操作队列。)您将前台模式主要应用于这样的情形:在次队列执行的工作,产生了必须在主队列执行的任务,例如更新用户界面的操作。

    • Category. A category offers you a way to extend a class by adding methods to it. As with delegation, it enables you to customize behavior without subclassing. Categories are a feature of Objective-C described in Write Objective-C Code.

      类别。类别提供了一种方式,通过将方法添加到一个类,以使该类得到扩展。与委托一样,它可以让您自定行为,而不子类化。类别是 Objective-C 的一个功能,在“编写 Objective-C 代码”中有说明。

    Back to Design Patterns 

  • 相关阅读:
    JS基础
    NodeJs实现他人项目实例
    Node.js在任意目录下使用express命令‘不是内部或外部命令’解决方法
    HTTP基本知识
    RESTful API
    基本概念和方法1
    Node.js--安装express以及创建第一个express项目(windows)
    Node-debug方法
    css3动画划过有一个框
    translate 动画不同时间飞入文字
  • 原文地址:https://www.cnblogs.com/patientAndPersist/p/3143909.html
Copyright © 2020-2023  润新知