Why Java inheritance is mostly harmful to _jsp programming

Source: Internet
Author: User
Tags constant inheritance

Most good designers avoid the plague by avoiding the use of implementation inheritance (extends relationship). In fact, 80% of the code should be written entirely with interfaces, not through extends. The "Java Design pattern" book details how to use interface inheritance instead of implementing inheritance. This article describes why designers do this.

Extends is harmful, perhaps not for Charles Manson at this level, but bad enough it should be avoided whenever possible. The "Java Design pattern" book takes a large part to discuss using interface inheritance instead of implementing inheritance.

Good designers in his code, most of the use of interface, rather than the specific base class. This article discusses why designers choose this and also introduces some of the interface based programming basics.

   interfaces (Interface) and classes (class)?

One time, I attended a meeting of a Java user group. During the meeting, Jams Gosling (father of Java) made a speech as a promoter. In the memorable Q&a section, he was asked: "If you reconstruct Java, what do you want to change?" ”。 "I want to abandon classes," he answered. After the laughter subsided, it explained that the real problem was not the class itself, but the inheritance (extends) relationship. Interface inheritance (implements relationship) is better. You should avoid implementing inheritance as much as possible.

Loss of flexibility

Why should you avoid implementing inheritance? The first problem is to explicitly use specific class names that you fix to a specific implementation, adding unnecessary difficulty to the underlying changes.

In the current agile programming approach, the core is the concept of concurrent design and development. Before you design the program in detail, you start programming. This technique is different from the traditional method----the traditional way is that the design should be completed before coding starts----But many successful projects have proven you can develop high quality code faster than traditional step-by-step methods. But at the heart of parallel development is the idea of flexibility. You have to write your code in some way so that the newly discovered requirements can be merged into existing code as painless as possible.

Rather than fulfilling the characteristics you may need, you only need to achieve the characteristics that you clearly require, and moderate the tolerance for change. If you don't have this kind of flexible, parallel development, that's simply not possible.

Programming for Inteface is the core of a flexible structure. To illustrate why, let's take a look at what happens when you use them. Consider the following code:

F ()
{
LinkedList list = new LinkedList ();
//...
g (list);
}

G (LinkedList list)
{
List.add (...);
G2 (list)
}

Suppose a requirement for a quick query is raised so that the linkedlist cannot be solved. You need to replace it with HashSet. In existing code, the change cannot be localized because you need to modify not only F () but also G () (It has linkedlist parameters), and g () any code that passes the list. Rewrite the code as follows:

F ()
{
Collection list = new LinkedList ();
//...
g (list);
}

G (Collection list)
{
List.add (...);
G2 (list)
}

This modifies the linked list into a hash and may simply replace the new LinkedList () with the new HashSet (). That's it. There is no other place to change.

As another example, compare the following two pieces of code:

F ()
{
Collection C = new HashSet ();
//...
g (c);
}

G (Collection C)
{
for (Iterator i = C.iterator (); I.hasnext ())
Do_something_with (I.next ());
}

And

F2 ()
{
Collection C = new HashSet ();
//...
G2 (C.iterator ());
}

G2 (Iterator i)
{
while (I.hasnext ())
Do_something_with (I.next ());
}


The G2 () method can now traverse the derivation of the collection, just as you can get the key-value pairs from the map. In fact, you can write iterator, which produces data instead of traversing a collection. You can write iterator, and it gets information from the frame or file of the test. This will be a great deal of flexibility.

   coupling

A more critical issue for implementing inheritance is coupling---irritating dependency, which is part of that program that is dependent on the other part. Global variables provide a classic example of why strong coupling can cause trouble. For example, if you change the type of a global variable, then all functions that use that variable may be affected, so all of the code is checked, changed, and tested again. Moreover, all functions that use this variable are coupled to each other through this variable. That is, if a variable value is changed when it is difficult to use, a function may incorrectly affect the behavior of another function. This problem is significantly hidden in multithreaded programs.

As a designer, you should try to minimize coupling relationships. You cannot eliminate coupling together, because the method invocation of objects from one class to another is a loosely coupled form. You can't have a program, it doesn't have any coupling. However, you can minimize certain coupling by adhering to the OO rules (most importantly, an object's implementation should be completely hidden from using his object). For example, an instance variable of an object (not a member domain of a constant) should always be private. I mean a period of time, no exceptions, constant. (You can use the protected method occasionally, but protected instance variables are odious) The same reason you should not get/ Set functions---they are simply too complex to be common to a domain (although the Access function that returns the decorated object instead of the base type value is in some cases the cause, in which case the returned object class is a critical abstraction at design time).

Here, I'm not bookish. In my own work, I found a direct correlation between the rigor of my OO approach, rapid code development, and easy code implementation. Whenever I violate the Central OO principle, such as implementation concealment, I result in rewriting that code (generally because the code is not debugged). I don't have time to rewrite the code, so I follow those rules. What I care about is completely practical? I'm not interested in clean reasons.

  vulnerable base class problems

Now let's apply the concept of coupling to inheritance. In a extends inheritance implementation system, derived classes are very tightly coupled to the base class, and when such tight joins are not expected. The designer has applied the nickname "Fragile base class problem" to describe the behavior. The underlying class is considered vulnerable because you modify the base class when it looks safe, but when you inherit from a derived class, the new behavior may cause the derived class to become dysfunctional. You cannot identify the base class changes by simply checking the base class in isolation, but you must also look (and test) all derived classes. Also, you have to check all the code, which is also used in base classes and derived class objects, because the code may be broken by new behavior. A simple change to the underlying class may cause the entire program to be unavailable.

Let's examine the problem of weak base class and base class coupling together. The following class extends the Java ArrayList class to make it run like a stack:

Class Stack extends ArrayList
{
private int stack_pointer = 0;

public void push (Object article)
{
Add (stack_pointer++, article);
}

Public Object pop ()
{
Return remove (--stack_pointer);
}

public void Push_many (object[] articles)
{
for (int i = 0; i < articles.length; ++i)
Push (Articles[i]);
}
}

Even a simple class like this has its problems. Thinking when a user balances inheritance and uses the ArrayList clear () method to pop the stack:

Stack a_stack = new stack ();
A_stack.push ("1");
A_stack.push ("2");
A_stack.clear ();

This code was compiled successfully, but because the base class does not know about the stack pointer stacks, the Stack object is currently in an undefined state. The next for push () calls puts the new item in the position of index 2. (Stack_pointer's current value), so the stack effectively has three elements-the bottom two are garbage. (This is the problem with Java's stack class, don't use it).

The solution to this annoying inherited method problem is to overwrite all ArrayList methods for the stack, which modifies the state of the array, overwriting the correct operation stack pointer or throwing an exception. (The RemoveRange () method is a good candidate for throwing an exception).

This method has two drawbacks. First, if you cover all the things, this base class should really be a interface, not a class. If you don't use any inheritance method, there is no such thing in implementing inheritance. Second, and more importantly, you can't allow a stack to support all the ArrayList methods. For example, annoying RemoveRange () has little effect. The only sensible way to implement a useless method is to make it an exception, since it should never be invoked. This method effectively makes the compilation error a run-time error. The bad thing is that if the method is just not defined, the compiler will output an error that is not found by a method. If the method exists, but throws an exception, you will be able to discover the invocation error only when the program is actually running.

A better solution to this base class problem is to encapsulate the data structure instead of using inheritance. This is the new and improved stack version:

Class Stack
{
private int stack_pointer = 0;
Private ArrayList The_data = new ArrayList ();

public void push (Object article)
{
The_data.add (stack_poniter++, article);
}

Public Object pop ()
{
Return The_data.remove (--stack_pointer);
}

public void Push_many (object[] articles)
{
for (int i = 0; i < o.length; ++i)
Push (Articles[i]);
}
}

It's been good so far, but considering the fragile base class problem, we say you want to create a variable in stack that tracks the maximum stack size over a period of time. A possible implementation might look like this:

Class Monitorable_stack extends Stack
{
private int high_water_mark = 0;
private int current_size;

public void push (Object article)
{
if (++current_size > High_water_mark)
High_water_mark = current_size;
Super.push (article);
}

Publish Object pop ()
{
--current_size;
return Super.pop ();
}

public int Maximum_size_so_far ()
{
return high_water_mark;
}
}

This new class works well, at least for a while. Unfortunately, this code has uncovered the fact that Push_many () runs by invoking push (). First of all, this detail does not look like a bad choice. It simplifies the code, and you can get a derived version of the push (), even when monitorable_stack is accessed through stack references so that High_water_mark can update correctly.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.