Object-Oriented Application Frameworks

原创 2006年06月22日 23:25:00

Object-Oriented Application Frameworks

Mohamed Fayad
Douglas C. Schmidt

The following is the guest editorial for the Communications of the ACM, Special Issue on Object-Oriented Application Frameworks, Vol. 40, No. 10, October 1997.

 

 


Computing power and network bandwidth have increased dramatically over the past decade. However, the design and implementation of complex software remains expensive and error-prone. Much of the cost and effort stems from the continuous re-discovery and re-invention of core concepts and components across the software industry. In particular, the growing heterogeneity of hardware architectures and diversity of operating system and communication platforms makes it hard to build correct, portable, efficient, and inexpensive applications from scratch.

Object-oriented (OO) application frameworks are a promising technology for reifying proven software designs and implementations in order to reduce the cost and improve the quality of software. A framework is a reusable, ``semi-complete'' application that can be specialized to produce custom applications [Johnson:88]. In contrast to earlier OO reuse techniques based on class libraries, frameworks are targeted for particular business units (such as data processing or cellular communications) and application domains (such as user interfaces or real-time avionics). Frameworks like MacApp, ET++, Interviews, ACE, Microsoft's MFC and DCOM, JavaSoft's RMI, and implementations of OMG's CORBA play an increasingly important role in contemporary software development.

 


The primary benefits of OO application frameworks stem from the modularity, reusability, extensibility, and inversion of control they provide to developers, as described below:

 

  • Modularity -- Frameworks enhance modularity by encapsulating volatile implementation details behind stable interfaces. Framework modularity helps improve software quality by localizing the impact of design and implementation changes. This localization reduces the effort required to understand and maintain existing software.

     

  • Reusability -- The stable interfaces provided by frameworks enhance reusability by defining generic components that can be reapplied to create new applications. Framework reusability leverages the domain knowledge and prior effort of experienced developers in order to avoid re-creating and re-validating common solutions to recurring application requirements and software design challenges. Reuse of framework components can yield substantial improvements in programmer productivity, as well as enhance the quality, performance, reliability and interoperability of software.

     

  • Extensibility -- A framework enhances extensibility by providing explicit hook methods [Pree:94] that allow applications to extend its stable interfaces. Hook methods systematically decouple the stable interfaces and behaviors of an application domain from the variations required by instantiations of an application in a particular context. Framework extensibility is essential to ensure timely customization of new application services and features.

     

  • Inversion of control -- The run-time architecture of a framework is characterized by an ``inversion of control.'' This architecture enables canonical application processing steps to be customized by event handler objects that are invoked via the framework's reactive dispatching mechanism. When events occur, the framework's dispatcher reacts by invoking hook methods on pre-registered handler objects, which perform application-specific processing on the events. Inversion of control allows the framework (rather than each application) to determine which set of application-specific methods to invoke in response to external events (such as window messages arriving from end-users or packets arriving on communication ports).

Developers in certain domains have successfully applied OO application frameworks for many years. Early object-oriented frameworks (such as MacApp and Interviews) originated in the domain of graphical user interfaces (GUIs). The Microsoft Foundation Classes (MFC) is a contemporary GUI framework that has become the de facto industry standard for creating graphical applications on PC platforms. Although MFC has limitations (such as lack of portability to non-PC platforms), its wide-spread adoption demonstrates the productivity benefits of reusing common frameworks to develop graphical business applications.

Application developers in more complex domains (such as telecommunications, distributed medical imaging, and real-time avionics) have traditionally lacked standard ``off-the-shelf'' frameworks. As a result, developers in these domains largely build, validate, and maintain software systems from scratch. In an era of deregulation and stiff global competition, however, it has become prohibitively costly and time consuming to develop applications entirely in-house from the ground up.

Fortunately, the next generation of OO application frameworks are targeting complex business and application domains. At the heart of this effort are Object Request Broker (ORB) frameworks, which facilitate communication between local and remote objects. ORB frameworks eliminate many tedious, error-prone, and non-portable aspects of creating and managing distributed applications and reusable service components. This enables programmers to develop and deploy complex applications rapidly and robustly, rather than wrestling endlessly with low-level infrastructure concerns. Widely used ORB frameworks include CORBA, DCOM, and Java RMI.

 


Although the benefits and design principles underlying frameworks are largely independent of domain to which they are applied, we've found it useful to classify frameworks by their scope, as follows:

 

  • System infrastructure frameworks -- These frameworks simplify the development of portable and efficient system infrastructure such as operating system [Campbell-Islam:93] and communication frameworks [Schmidt:97], and frameworks for user interfaces and language processing tools. System infrastructure frameworks are primarily used internally within a software organization and are not sold to customers directly.

     

  • Middleware integration frameworks -- These frameworks are commonly used to integrate distributed applications and components. Middleware integration frameworks are designed to enhance the ability of software developers to modularize, reuse, and extend their software infrastructure to work seamlessly in a distributed environment. There is a thriving market for Middleware integration frameworks, which are rapidly becoming commodities. Common examples include ORB frameworks, message-oriented middleware, and transactional databases.

     

  • Enterprise application frameworks -- These frameworks address broad application domains (such as telecommunications, avionics, manufacturing, and financial engineering [Birrer:93]) and are the cornerstone of enterprise business activities [Fayad-Hamu:97]. Relative to System infrastructure and Middleware integration frameworks, Enterprise frameworks are expensive to develop and/or purchase. However, Enterprise frameworks can provide a substantial return on investment since they support the development of end-user applications and products directly. In contrast, System infrastructure and Middleware integration frameworks focus largely on internal software development concerns. Although these frameworks are essential to rapidly create high quality sofware, they typically don't generate substantial revenue for large enterprises. As a result, it's often more cost effective to buy System infrastructure and Middleware integration frameworks rather than build them in-house [Fayad-Hmau:97].

     

Regardless of their scope, frameworks can also be classified by the techniques used to extend them, which range along a continuum from whitebox frameworks to blackbox frameworks. Whitebox frameworks rely heavily on OO language features like inheritance and dynamic binding to achieve extensibilty. Existing functionality is reused and extended by (1) inheriting from framework base classes and (2) overriding pre-defined hook methods using patterns like Template Method [Gamma:95]. Blackbox frameworks support extensibility by defining interfaces for components that can be plugged into the framework via object composition. Existing functionality is reused by (1) defining components that conform to a particular interface and (2) integrating these components into the framework using patterns like Strategy [Gamma:95] and Functor.

Whitebox frameworks require application developers to have intimate knowledge of the frameworks' internal structure. Although whitebox frameworks are widely used, they tend to produce systems that are tightly coupled to the specific details of the framework's inheritance hierarchies. In contrast, blackbox frameworks are structured using object composition and delegation more than inheritance. As a result, blackbox frameworks are generally easier to use and extend than whitebox frameworks. However, blackbox frameworks are more difficult to develop since they require framework developers to define interfaces and hooks that anticipate a wider range of potential use-cases [Johnson:95].

 


Frameworks are closely related to other approaches to reuse, including:

 

  • Patterns -- Patterns represent recurring solutions to software development problems within a particular context. Patterns and frameworks both facilitate reuse by capturing successful software development strategies. The primary difference is that frameworks focus on reuse of concrete designs, algorithms, and implementations in a particular programming language. In contrast, patterns focus on reuse of abstract designs and software micro-architectures.

    Frameworks can be viewed as a concrete reification of families of design patterns that are targeted for a particular application-domain. Likewise, design patterns can be viewed as more abstract micro-architectural elements of frameworks that document and motivate the semantics of frameworks in an effective way. When patterns are used to structure and document frameworks, nearly every class in the framework plays a well-defined role and collaborates effectively with other classes in the framework.

     

  • Class libraries -- Frameworks extend the benefits of OO class libraries in the following ways:

     

    • Frameworks define ``semi-complete'' applications that embody domain-specific object structures and functionality -- Components in a framework work together to provide a generic architectural skeleton for a family of related applications. Complete applications can be composed by inheriting from and/or instantiating framework components. In contrast, class libraries are less domain-specific and provide a smaller scope of reuse. For instance, class library components like classes for Strings, complex numbers, arrays, and bitsets are relatively low-level and ubiquitous across many application domains.

       

    • Frameworks are active and exhibit ``inversion of control'' at run-time -- Class libraries are typically passive, i.e., they perform their processing by borrowing threads of control from self-directed application objects. In contrast, frameworks are active, i.e., they control the flow of control within an application via event dispatching patterns like Reactor and Observer. The ``inversion of control'' in the run-time architecture of a framework is often referred to as The Hollywood Principle, i.e., ``Don't call us, we'll call you.''

       

    In practice, frameworks and class libraries are complementary technologies. For instance, frameworks tyically utilize class libraries like the C++ Standard Template Library (STL) internally to simplify the development of the framework. Likewise, application-specific code invoked by framework event handlers can utilize class libraries to perform basic tasks such as string processing, file management, and numerical analysis.

     

  • Components -- Components are self-contained instances of abstract data types (ADTs) that can be plugged together to form complete applications. Common examples of components include VBX controls and CORBA Object Services. In terms of OO design, a component is a blackbox that defines a cohesive set of operations, which can be reused based solely upon knowledge of the syntax and semantics of its interface. Compared with frameworks, components are less tightly coupled and can support binary-level reuse. For example, applications can reuse components without having to subclass from existing base classes.

    The relationship between frameworks and components is highly synergistic, with neither subordinate to the other. Frameworks can be used to develop components, whereby the component interface provides a Facade for the internal class structure of the framework. Likewise, components can be used as pluggable strategies in blackbox frameworks. In general, frameworks are often used to simplify the development of infrastructure and middleware software, whereas components are often used to simplify the development of end-user application software. Naturally, components are also effective for developing infrastructure and middleware, as well.


When used in conjunction with patterns, class libraries, and components, OO application frameworks can significantly increase software quality and reduce development effort. However, a number of challenges must be addressed in order to employ frameworks effectively. Companies attempting to build or use large-scale reusable framework often fail unless they recognize and resolve challenges such as development effort, learning curve, integratability, maintainability, validation and defect removal, efficiency, and lack of standards, which are outlined below:

 

  • Development effort -- While developing complex software is hard enough, developing high quality, extensible, and reusable frameworks for complex application domains is even harder. The skills required to produce frameworks successfully often remain locked in the heads of expert developers. One of the goals of this theme issue is to demystify the software process and design principles associated with developing and using frameworks.

     

  • Learning curve -- Learning to use an OO application framework effectively requires considerable investment of effort. For instance, it often takes 6-12 months become highly productive with a GUI framework like MFC or MacApp, depending on the experience of developers. Typically, hands-on mentoring and training courses are required to teach application developers how to use the framework effectively. Unless the effort required to learn the framework can be amortized over many projects, this investment may not be cost effective. Moreover, the suitability of a framework for a particular application may not be apparent until the learning curve has flattened.

     

  • Integratability -- Application development will be increasingly based on the integration of multiple frameworks (e.g. GUIs, communication systems, databases, etc.) together with class libraries, legacy systems, and existing components. However, many earlier generation frameworks were designed for internal extension rather than for integration with other frameworks developed externally. Integration problems arise at several levels of abstraction, ranging from documentation issues [Fayad-Hamu 97], to the concurrency/distribution architecture, to the event dispatching model. For instance, while inversion of control is an essential feature of a framework, integrating frameworks whose event loops are not designed to interoperate with other frameworks is hard.

     

  • Maintainability -- Application requirements change frequently. Therefore, the requirements of frameworks often change, as well. As frameworks invariably evolve, the applications that use them must evolve with them.

    Framework maintenance activities include modification and adaptation of the framework. Both modification and adaptation may occur on the functional level (i.e., certain framework functionality does not fully meet developers' requirements), as well as on the non-functional level (which includes more qualitative aspects such as portability or reusability).

    Framework maintenance may take different forms, such as adding functionality, removing functionality, and generalization. A deep understanding of the framework components and their interrelationships is essential to perform this task successfully. In some cases, the application developers and/or the end-users must rely entirely on framework developers to maintain the framework.

     

  • Validation and defect removal -- Although a well-designed, modular framework can localize the impact of software defects, validating and debugging applications built using frameworks can be tricky for the following reasons:

     

    • Generic components are harder to validate in the abstract -- A well-designed framework component typically abstracts away from application-specific details, which are provided via subclassing, object composition, or template parameterization. While this improves the flexibility and extensibility of the framework, it greatly complicates module testing since the components cannot be validated in isolation from their specific instantiations.

      Moreover, it is usually hard to distinguish bugs in the framework from bugs in application code. As with any software development, bugs are introduced into a framework from many possible sources, such as failure to understand the requirements, overly coupled design, or an incorrect implementation. When customizing the components in framework to a particular application, the number of possible error sources will increase.

       

    • Inversion of control and lack of explicit control flow -- Applications written with frameworks can be hard to debug since the framework's ``inverted'' flow of control oscillates between the application-independent framework infrastructure and the application-specific method callbacks. This increases the difficulty of ``single-stepping'' through the run-time behavior of a framework within a debugger since the control flow of the application is driven implicitly by callbacks and developers may not understand or have access to the framework code. This is similar to the problems encountered trying to debug a compiler lexical analyser and parser written with LEX and YACC. In these applications, debugging is straightforward when the thread of control is in the user-defined action routines. Once the thread of control returns to the generated DFA skeleton, however, it is hard to trace the program's logic.

       

  • Efficiency -- Frameworks enhance extensibility by employing additional levels of indirection. For instance, dynamic binding is commonly used to allow developers to subclass and customize existing interfaces. However, the resulting generality and flexibility often reduce efficiency. For instance, in languages like C++ and Java, the use of dynamic binding makes it impractical to support Concrete Data Types (CDTs), which are often required for time-critical software. The lack of CDTs yields (1) an increase in storage layout (e.g., due to embedded pointers to virtual tables), (2) performance degradation (e.g. due to the additional overhead of invoking a dynamically bound method and the inability to inline small methods), and (3) a lack of flexibility (e.g., due to the inability to place objects in shared memory).

     

  • Lack of standards -- Currently, there are no widely accepted standards for designing, implementing, documenting, and adapting frameworks. Moreover, emerging industry standard frameworks (such as CORBA, DCOM, and Java RMI) currently lack the semantics, features, and interoperability to be truly effective across multiple application domains. Often, vendors use industry standards to sell proprietary software under the guise of open systems. Therefore, it's essential for companies and developers to work with standards organizations and middleware vendors to ensure the emerging specifications support true interoperability and define features that meet their software needs.

     


Over the next several years, we expect the following framework-related topics will receive considerable attention by researchers and developers:

 

  • Reducing framework development effort -- Traditionally, reusable frameworks have been developed by generalizing from existing systems and applications. Unfortunately, this incremental process of organic development is often slow and unpredictable since core framework design principles and patterns must be discovered ``bottom-up.'' However, since many good framework exemplars now exist, we expect that the next generation of developers will leverage this collective knowledge to conceive, design, and implement higher quality frameworks more rapidly.

     

  • Greater focus on domain-specific enterprise frameworks -- Existing frameworks have focused largely on system infrastructure and middleware integration domains (such as user interfaces [Gamma:95,Pree:94] and OS/communication systems [Schmidt:97,Johnson:95,Cambell-Islam:93]). In contrast, there are relatively few widely documented exemplars of enterprise frameworks for key business domains such as manufacturing, banking, insurance, and medical systems. As more experience is gained developing frameworks for these business domains, however, we expect that the collective knowledge of frameworks will be expanded to cover an increasing wide range of domain-specific topics and an increasing number of Enterprise application frameworks will be produced. As a result, benefits of frameworks will become more immediate to application programmers, as well as to infrastructure developers.

     

  • Blackbox frameworks -- Many framework experts [Johnson:88] favor black-box frameworks over white-box frameworks since black-box frameworks emphasize dynamic object relationships (via patterns like Bridge and Strategy Gamma:95) rather than static class relationships. Thus, it is easier to extend and reconfigure black-box frameworks dynamically. As developers become more familiar with techniques and patterns for factoring out common interfaces and components, we expect that an increasing percentage of black-box frameworks will be produced.

     

  • Framework documentation -- Accurate and comprehensible documentation is crucial to the success of large-scale frameworks. However, documenting frameworks is a costly activity and contemporary tools often focus on low-level method-oriented documentation, which fails to capture the strategic roles and collaborations among framework components. We expect that the advent of tools for reverse-engineering the structure of classes and objects in complex frameworks will help to improve the accuracy and utility of framework documentation. Likewise, we expect to see an increase in the current trend [Johnson:95,Schmidt:97] of using design patterns to provide higher-level descriptions of frameworks.

     

  • Processes for managing framework development -- Frameworks are inherently abstract since they generalize from a solution to a particular application challenge to provide a family of solutions. This level of abstraction makes it difficult to engineer their quality and manage their production. Therefore, it is essential to capture and articulate development processes that can ensure the successful development and use of frameworks. We believe that extensive prototyping and phased introduction of framework technology into organizations is crucial to reducing risk and helping to ensure successful adoption.

     

  • Framework economics -- The economics of developing framework includes activities such as the following:

     

    • Determining effective framework cost metrics -- which measure the savings of reusing framework components vs. building applications from scratch;

       

    • Cost estimation -- which is the activity of accurately forecasting the cost of buying, building, or adapting a particular framework;

       

    • Investment analysis and justification -- which determines the benefits of applying frameworks in terms of return on investment;

       

    We expect that the focus on framework economics will help to bridge the gap among the technical, managerial, and financial aspects of making, buying, or adapting frameworks [Hamu-Fayad:97].

The articles in this theme issue describe how OO application frameworks provide a powerful vehicle for reuse, as well as a way to capture the essence of successful patterns, architectures, components, policies, services, and programming mechanisms. The feature articles lead off with ``Framework Development for Large Systems'' by Dirk Baumer, Guido Gryczan, Rolf Knoll, Carola Lilienthal, Dirk Riehle, and Heinz Zullighoven. These authors draw on their experience developing large-scale industrial banking projects to present concepts and techniques for domain partitioning, framework layering and framework construction. The second feature article on "Evolving Custom-Made Applications into Domain-Specific Frameworks" by Wim Codenie, Koen De Hondt, Patrick Steyaert, Arlette Vercammen discusses solutions to common framework development challenges such as avoiding the proliferation of versions, estimating effort, and alleviating the tendency towards ``architectural drift.''

The Theme section also contains several short articles, starting with "Frameworks = (Patterns + Components)/2" by Ralph Johnson, which compares and contrasts frameworks with other object-oriented reuse techniques such as patterns and components. The second short article is "An Adaptive Framework for Developing Multimedia Software Components" by Edward Posnak, Greg Lavender, and Harrick Vin describes a framework that simplifies the development of dynamically adaptive multimedia software components by promoting the reuse of code, design patterns, and domain expertise. The next short article is "Frameworks Design by Systematic Generalization with Hot Spots and Patterns" by Hans Albrecht Schmid, which presents a systematic method for designing frameworks based on identifying "hot spots," which capture key sources of variation in an application domain. Serge Demeyer, Theo Meijler, Oscar Nierstrasz, and Patrick Steyaert also focus on hot spots in their article on "Design Guidelines for Tailorable Frameworks," which presents design guidelines to develop frameworks for open systems.

Several case studies are also covered in the theme issues, including "The Framework Life Span: a Case Study for Flexible Manufacturing Systems" by A. Aarsten, Davide Brugali, and G. Menga, which highlights the relationships between application frameworks, patterns, and pattern languages in the domain of manufacturing systems. Likewise, the SEMATECH CIM Framework, by David Doscher and Robert Hodges describes the structure of a framework for computer integrated manufacturing of semiconductors. In addition, Adele Goldberg, Steve Abell, and David Leibs describe LearningWorks, which is a framework for exploring ideas about computing and software system construction.

Finally, the theme section contains three sidebars: "Achieve Bottom-Line Improvements with Enterprise Frameworks" by Hamu and Fayad, "Framework Integration problems, Causes, and Solutions by M. Mattsson et al., and "Lessons Learned Building Reusable OO Frameworks for Distributed Software" by Schmidt and Fayad.

 


The articles in this theme issue reinforce our believe that object-oriented application frameworks will be at the core of leading-edge software technology in the twenty-first century. As software systems become increasingly complex, object-oriented application frameworks are becoming increasingly important for industry and academia. The extensive focus on application frameworks in the object-oriented community offers software developers an important vehicle for reuse and a means to capture the essence of successful patterns, architectures, components, and programming mechanisms.

The good news is that framework are becoming mainstream and developers at all levels are increasingly adopting and succeeding with framework technologies. However, OO application frameworks are ultimately only as good as the people who build and use them. Creating robust, efficient, and reusable application frameworks requires development teams with a wide range of skills. We need expert analysts and designers who have mastered patterns, software architectures, and protocols in order to alleviate the inherent and accidental complexities of complex software. Likewise, we need expert middleware developers who can implement these patterns, architectures, and protocols within reusable frameworks. In addition, we need application programmers who have the motivation, skills, and training to learn how to use these frameworks effectively. We encourage you to get involved with others working on frameworks by attending conferences, participating in online mailing lists and newsgroups, and contributing your insights and experience.

 

 


 

Acknowledgements

We would like to thank the staff of CACM for their help in producing this theme issue. We'd also like to thank Ralph Johnson and Bryan Doerr for suggestions on improving this introduction. In addition, we are grateful to all the reviewers for valuable and useful reviews on all the submissions for this theme section. v

 


 

References

[Birrer:93] Eggenschwiler T. Birrer "Frameworks in the Financial Engineering Domain: An Experience Report" ECOOP '93 Proceedings, Lecture Notes in Computer Science nr. 707, Springer-Verlag, 1993.

[Johnson:88] Ralph Johnson and Brian Foote. ``Designing Reusable Classes.'' Journal of Object-Oriented Programming. SIGS, 1, 5 (June/July. 1988), 22-35.

[Campbell-Islam:93] Roy H. Campbell and Nayeem Islam "A Technique for Documenting the Framework of an Object-Oriented System", Computing Systems, Vol. 6, No. 4, Fall 1993

[Fayad-Hamu:97] Mohamed E. Fayad and David S. Hamu "Object-Oriented Enterprise Frameworks: Make vs. Buy Decisions and Guidelines for Selection", The Communications of ACM, 1997, to appear.

[Gamma:95] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, "Design Patterns: Elements of Reusable Software Architecture", Addison-Wesley, 1995.

[Hamu-Fayad:97] David S. Hamu and Mohamed E. Fayad, "Achieve Bottom-Line Improvements with Enterprise Frameworks," The Communications of ACM, October 1997.

[Johnson:95] Herman Hueni and Ralph Johnson and Robert Engel, ``A Framework for Network Protocol Software,'' Proceedings of OOPSLA, Austin, Texas, October 1995.

[Pree:94] Wolfgang Pree, Design Patterns for Object-Oriented Software Development, Addison-Wesley, Reading, MA, 1994.

[Schmidt:97] Douglas C. Schmidt, ``Applying Design Patterns and Frameworks to Develop Object-Oriented Communication Software,'' Handbook of Programming Languages}, Volume I, edited by Peter Salus, MacMillan Computer Publishing, 1997.

 


Back to ACE home page.

Last modified 14:17:09 CDT 26 May 2004

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务和测试程序

在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务       在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。今...

为Android系统的Application Frameworks层增加硬件访问服务

在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。今天大家对iOS和Android系统的趋之若鹜,一定程度上是由于这两个系统上有着丰富多彩的各种应用软件。因此,软件和硬件的关系...

在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务

在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务         我们在Android系统增加硬件服务的目的是为了让应用层...

在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务

转自 http://blog.csdn.net/luoshengyang/article/details/6578352  在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。...

在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务

我们在Android系统增加硬件服务的目的是为了让应用层的APP能够通过Java接口来访问硬件服务。那么, APP如何通过Java接口来访问Application Frameworks层提供的硬件服务...

在Ubuntu上为Android系统的Application Frameworks层增加硬件访问服务

在数字科技日新月异的今天,软件和硬件的完美结合,造就了智能移动设备的流行。今天大家对iOS和Android系统的趋之若鹜,一定程度上是由于这两个系统上有着丰富多彩的各种应用软件。因此,软件和硬件的关系...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)