> Java > Java베이스 > 디자인 패턴의 6가지 원칙은 무엇입니까?

디자인 패턴의 6가지 원칙은 무엇입니까?

青灯夜游
풀어 주다: 2023-02-22 11:32:06
원래의
19061명이 탐색했습니다.

디자인 패턴의 6가지 원칙: 1. 단일 책임 원칙, 그 핵심은 클래스의 세분성을 제어하고 개체를 분리하며 응집력을 향상시키는 것입니다. 2. "를 통해 달성할 수 있는 열기 및 닫기 원칙" 추상 제약 조건 및 캡슐화 변경" 달성하기 위해; 3. Liskov 대체 원칙은 주로 상속에 대한 일부 원칙을 설명합니다. 4. 종속성 반전 원칙은 클라이언트와 구현 모듈 간의 결합을 줄입니다. 5. 인터페이스 격리 원칙은 인터페이스를 제한하고 클래스를 줄이는 것입니다. 6. 데메테르의 법칙은 소프트웨어 엔터티 간의 통신 폭과 깊이를 제한해야 합니다.

디자인 패턴의 6가지 원칙은 무엇입니까?

이 튜토리얼의 운영 환경: windows7 시스템, java8 버전, DELL G3 컴퓨터.

디자인 패턴에 관해서는 오래전에 많은 디자인 패턴 책을 읽었고, 그중 일부는 여러 번 읽었으며, 코딩할 때 이러한 디자인 패턴을 사용할 수 있기를 항상 바랐습니다. 그러나 일상적인 코딩에서는 싱글톤이 가장 많이 사용되고, 그 다음으로 옵저버 패턴과 빌더 패턴(빌더)이 더 자주 사용되며 나머지는 거의 사용되지 않습니다.

우리가 사용할 수 없는 이유는 우리가 아직 디자인 패턴의 개념을 이해하지 못하고 이러한 디자인 패턴을 코딩에서 발생하는 문제와 연결하지 못하기 때문에 디자인 패턴을 사용할 수 없기 때문입니다.

사실 디자인 패턴은 공통적인 문제를 해결하기 위해 제안됩니다. 그렇다면 어떤 디자인 패턴을 채택할지 고민할 때 먼저 현재의 문제가 무엇인지 자문해 보아야 합니다. 문제에 따라 적절한 디자인 패턴을 선택하십시오.

디자인 패턴에 익숙해지면 일부 디자인 패턴 사이에는 포괄적인 관계가 있으며 매우 유사하지만 서로 다른 디자인 패턴은 서로 다른 문제를 해결한다는 사실을 알게 됩니다.

모듈을 설계할 때 다음과 같은 관점에서 고려할 수 있습니다.

  • 이 모듈과 다른 모듈의 관계는 무엇입니까?

  • 모듈의 어떤 부분이 변하지 않고, 어떤 부분이 지속적으로 바뀌고, 어떻게 바뀌나요?

  • 클래스 간의 관계는 무엇입니까? 왜 의존해야 하나요?

  • 인터페이스를 추가하시겠습니까? 인터페이스는 어떤 문제를 해결하기 위해 존재하는가?

물론 이 글에서는 디자인 패턴 사용법을 가르치지 않습니다. 오히려 디자인 패턴의 디자인 원리를 설명합니다. 디자인 패턴도 디자인할 때 몇 가지 규칙을 따릅니다.

디자인 패턴의 6가지 원칙, 구체적으로는 다음과 같습니다.

  • 단일 책임 원칙(클래스 및 메서드, 인터페이스)

  • 열기 및 닫기 원칙(확장 가능, 수정 가능)

  • Richter 대체 원칙(기본 클래스와 하위 클래스 간의 관계)

  • 종속성 반전 원칙(구체적 객체 대신 추상 인터페이스 사용)

  • 인터페이스 격리 원칙(인터페이스는 기능에 따라 세분화됨)

  • 데메테르의 법칙(클래스 간의 긴밀한 관계)

각 디자인 원칙 옆에는 괄호가 있는데, 이는 적용 범위를 설명하거나 설명하는 데 사용됩니다. 각 원칙은 아래에 자세히 설명되어 있습니다.

단일 책임 원칙(클래스, 메서드, 인터페이스)의 정의


단일 책임 원칙(SRP)을 단일 기능 원칙이라고도 합니다. 여기서 책임은 클래스 변경 이유를 의미합니다. 단일 책임 원칙은 클래스가 변경 이유를 하나만 갖고, 그렇지 않으면 클래스를 분할해야 함을 규정합니다. 변경).

이 원칙은 객체가 너무 많은 책임을 맡아서는 안 된다고 명시하고 있습니다. 객체가 너무 많은 책임을 맡으면 최소한 다음 두 가지 단점이 있습니다.

  • 하나의 책임이 변경되면 이 클래스가 약화되거나 방해받을 수 있습니다. 다른 책임 구현

  • 클라이언트가 객체의 특정 책임을 필요로 할 때 다른 불필요한 책임을 모두 포함해야 하므로 코드가 중복되거나 낭비됩니다.

단일 책임 원칙의 장점

단일 책임 원칙의 핵심은 클래스의 세분성을 제어하고 객체를 분리하며 응집력을 향상시키는 것입니다. 단일 책임 원칙을 따르면 다음과 같은 이점이 있습니다.

  • 수업의 복잡성을 줄이세요. 클래스가 단 하나의 책임만 담당하는 경우 해당 클래스의 논리는 여러 책임을 담당하는 경우보다 확실히 훨씬 간단합니다.

  • 클래스의 가독성을 향상하세요. 복잡성은 줄어들고, 가독성은 자연스럽게 향상됩니다.

  • 시스템의 유지 관리성을 향상시킵니다. 가독성이 향상되면 자연스럽게 유지 관리가 더 쉬워집니다.

  • 변경으로 인한 위험 감소. 변화는 불가피합니다. 단일 책임 원칙을 잘 따르면 하나의 기능이 수정되면 다른 기능에 미치는 영향을 크게 줄일 수 있습니다.

단일 책임 원칙을 구현하는 방법

단일 책임 원칙은 적용하기 가장 간단하지만 가장 어려운 원칙입니다. 디자이너는 클래스의 다양한 책임을 찾아 분리한 다음 캡슐화해야 합니다. 그것들을 다른 클래스나 모듈로 나누세요. 디스커버리 클래스의 다양한 책임은 디자이너에게 강력한 분석 및 디자인 기능과 관련 리팩토링 경험을 요구합니다.

Example

public interface UserService {
    
    public void login(String username, String password);
    public void register(String email, String username, String password);
    public void logError(String msg);
    public void sendEmail(String email);
    
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

이 코드는 확실히 사용자 등록과 로그인뿐만 아니라 로그 기록과 이메일 전송까지 담당하는데 큰 문제가 있습니다. 후자의 행동은 전자의 행동과 크게 다릅니다. <br/>这段代码很显然存在很大的问题,UserService 既要负责用户的注册和登录,还要负责日志的记录和邮件的发送,并且后者的行为明显区别于前者。<br/>

假设我要修改发送邮件的逻辑就得修改这个类,这时候 qa 还得回归登录注册逻辑,这样明显不合理。

因此我们需要进行拆分,根据具体的职能可将其具体拆分如下:

UserService:只负责登录注册

public interface UserService {

    public void login(String username, String password);
    public void register(String email, String username, String password);

}
로그인 후 복사

LogService :只负责日志<br/>

public interface LogService {
    public void logError(String msg);

}
로그인 후 복사

EmailService: 只负责发送邮件

public interface EmailService {
    public void sendEmail(String email);

}
로그인 후 복사

这时候,咱们再去回顾前面提到的优点,就能深深体会了。

这里只是讲了接口,其实对类也一样,甚至方法也是一样的。

对于类来说,根据类名,确保里面提供的方法都是属于这个类的。

对于方法,不要把不相关的对象实例作为参数传进来。如果你发现某个方法依赖某个不相关的对象,那么这个方法的实现可能就存在问题。

比如 android 中图片下载后显示到 imageView 中,我提供如下的方法:

loadImage(String url, ImageView view) {
// 下载图片,展示图片
}
로그인 후 복사

对于 loadImage 这个方法,参数 url 是ok 的,但是参数 ImageView 却是不合理的。因为这里做了两个操作,下载图片,展示图片。应该将这个方法在进行拆分:

// 下载图片 
loadImage(String url) {

}
// 显示图片
displayImage(String url, ImageView view) {

// 调用 getBitmap (url)  获取图片
// 获取图片后将其设置到 view 中。

}

// 根据 url 获取图片, 
getBitmap(String url) {

}
로그인 후 복사

这样整个逻辑就很清晰。后续需要修改下载逻辑,也不会影响到展示逻辑。当然其实还有个问题是,这两个方法要不要放在一个类里面?

开闭原则


开闭原则的实现方法:可以通过“抽象约束、封装变化”来实现开闭原则,即通过接口或者抽象类为软件实体定义一个相对稳定的抽象层,而将相同的可变因素封装在相同的具体实现类中。

因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节可以从抽象派生来的实现类来进行扩展,当软件需要发生变化时,只需要根据需求重新派生一个实现类来扩展就可以了。

示例

// 矩形
public class Rectangle {

    public double getWidth() {
        return width;
    }
    
    public double getHeight() {
        return height;
    }

}
로그인 후 복사

需要计算矩形的面积

// 面积计算器
public class AreaCalculator {

    public double area(Rectangle shape){
        return shape.getWidth() * shape.getHeight();
    }
}
로그인 후 복사

假设这时候,又多了一个圆形类

// 圆形
public class Circular {

    public double getRadius(){
        return radius;
    }
}
로그인 후 복사

同样也需要计算他的面积,这时候就会变成下面这样子:

public class AreaCalculator {

    public double area(Object shape){
        if(shape instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) shape;
            return rectangle.getWidth() * rectangle.getHeight();
        } else if (shape instanceof Circular) {
            Circular circular = (Circular) shape;
            return circular.getRadius() * circular.getRadius() * Math.PI;
        } else {
            throw new RuntimeException("There is no such type.");
        }
    }
}
로그인 후 복사

这么更改完成,完全没有问题。但是在真实的生产环境中,情况更为复杂,更改涉及的部分较多,那样就可能导致牵一发动全身。并且,以前编写的经过测试的一些功能需要重新测试,甚至导致某些功能不可用。

改进版,把计算面积这个公有逻辑变成一个接口:

public interface Shape {

    public double getArea();

}
 
public class Rectangle implements Shape{

    public double getWidth() {
        return width;
    }

    public double getHeight() {
        return height;
    }

    public double getArea() {
        return getWidth() * getHeight();
    }
    
}
로그인 후 복사

这样,当需求变更,需要计算圆形面积的时候,我们只需创建一个圆形的类,并实现 Shape 接口即可:<br/>

이메일 전송 로직을 수정하고 싶다고 가정하면, 이때 이 클래스를 수정해야 합니다. 분명히 불합리한 로그인 및 등록 논리.
그래서 분할해야 합니다. 특정 기능에 따라 다음과 같이 분할할 수 있습니다. 🎜🎜🎜UserService🎜: 로그인 및 등록만 담당🎜🎜
public class Circular implements Shape {

    public double getRadius(){
        return radius;
    }

    public double getArea() {
        return getRadius() * getRadius() * Math.PI;
    }
}
로그인 후 복사
🎜🎜🎜LogService: 🎜로깅만 담당<br/>🎜🎜
class Customer {
    public void shopping(ShaoguanShop shop) {
        //购物
        System.out.println(shop.sell());
    }
}
로그인 후 복사
로그인 후 복사
🎜🎜🎜EmailService🎜: 이메일 전송만 담당🎜🎜
class Customer {
    public void shopping(WuyuanShop shop) {
        //购物
        System.out.println(shop.sell());
    }
}
로그인 후 복사
로그인 후 복사
🎜🎜 이때 위에서 언급한 장점들을 복습하시면 깊이 이해가 되실 것입니다. 🎜🎜여기서는 인터페이스만 이야기했습니다. 사실 클래스도 마찬가지고, 메소드도 마찬가지입니다. 🎜🎜클래스의 경우 클래스 이름을 기준으로 클래스에 제공된 모든 메서드가 이 클래스에 속하는지 확인하세요. 🎜🎜메서드의 경우 관련 없는 객체 인스턴스를 매개변수로 전달하지 마세요. 메서드가 관련 없는 개체에 종속되어 있는 경우 메서드 구현에 문제가 있을 수 있습니다. 🎜🎜예를 들어 안드로이드에서 이미지를 다운로드하고 imageView에 표시한 후 다음 메소드를 제공합니다. 🎜🎜
class Customer {
    public void shopping(Shop shop) {
        //购物
        System.out.println(shop.sell());
    }
}

class Customer {
    public void shopping(Shop shop) {
        //购物
        System.out.println(shop.sell());
    }
}
로그인 후 복사
로그인 후 복사
🎜🎜loadImage 메소드의 경우 url 매개변수는 괜찮지만 ImageView 매개변수는 무리입니다. 여기서는 사진 다운로드와 사진 표시라는 두 가지 작업이 수행되기 때문입니다. 이 방법은 전체 논리가 명확하도록 🎜🎜
package principle;
public class DIPtest
{
    public static void main(String[] args)
    {
        Customer wang=new Customer();
        System.out.println("顾客购买以下商品:"); 
        wang.shopping(new ShaoguanShop()); 
        wang.shopping(new WuyuanShop());
    }
}
//商店
interface Shop
{
    public String sell(); //卖
}
//韶关网店
class ShaoguanShop implements Shop
{
    public String sell()
    {
        return "韶关土特产:香菇、木耳……"; 
    } 
}
//婺源网店
class WuyuanShop implements Shop
{
    public String sell()
    {
        return "婺源土特产:绿茶、酒糟鱼……"; 
    }
} 
//顾客
class Customer
{
    public void shopping(Shop shop)
    {
        //购物
        System.out.println(shop.sell()); 
    }
}
로그인 후 복사
로그인 후 복사
🎜🎜로 분할되어야 합니다. 나중에 다운로드 로직을 수정해야 하는 경우 디스플레이 로직에는 영향을 미치지 않습니다. 물론 실제로는 또 다른 질문이 있습니다. 이 두 메서드를 클래스에 배치해야 할까요? 🎜

🎜열림 및 닫힘 원리🎜


🎜열림 및 닫힘 원리 구현 방법: 열림 및 닫힘 원리는 "추상적 제약 조건 및 캡슐화 변경", 즉 소프트웨어 정의를 통해 실현할 수 있습니다. 인터페이스 또는 추상 클래스를 통한 엔터티 동일한 구체적인 구현 클래스에 동일한 가변 요소를 캡슐화하는 비교적 안정적인 추상화 계층입니다.
🎜🎜추상화는 유연성이 좋고 적응성이 넓기 때문에 추상화가 합리적이면 기본적으로 소프트웨어 아키텍처의 안정성을 유지할 수 있습니다. 소프트웨어의 휘발성 세부 사항은 추상화에서 파생된 구현 클래스에서 확장될 수 있습니다. 소프트웨어를 변경해야 하는 경우 확장 요구 사항에 따라 구현 클래스를 다시 파생하기만 하면 됩니다. 🎜🎜🎜Example🎜🎜🎜
顾客购买以下商品:
韶关土特产:香菇、木耳……
婺源土特产:绿茶、酒糟鱼……
로그인 후 복사
로그인 후 복사
🎜🎜직사각형의 면적을 계산해야 합니다🎜🎜
public interface UserService {
    
    public void login(String username, String password);
    public void register(String email, String username, String password);
    public void logError(String msg);
    public void sendEmail(String email);
    
}
로그인 후 복사
로그인 후 복사
로그인 후 복사
🎜🎜이때 또 다른 원 클래스가 있다고 가정해 보겠습니다. 🎜🎜
package principle;
public class LoDtest
{
    public static void main(String[] args)
    {
        Agent agent=new Agent();
        agent.setStar(new Star("林心如"));
        agent.setFans(new Fans("粉丝韩丞"));
        agent.setCompany(new Company("中国传媒有限公司"));
        agent.meeting();
        agent.business();
    }
}
//经纪人
class Agent
{
    private Star myStar;
    private Fans myFans;
    private Company myCompany;
    public void setStar(Star myStar)
    {
        this.myStar=myStar;
    }
    public void setFans(Fans myFans)
    {
        this.myFans=myFans;
    }
    public void setCompany(Company myCompany)
    {
        this.myCompany=myCompany;
    }
    public void meeting()
    {
        System.out.println(myFans.getName()+"与明星"+myStar.getName()+"见面了。");
    }
    public void business()
    {
        System.out.println(myCompany.getName()+"与明星"+myStar.getName()+"洽淡业务。");
    }
}
//明星
class Star
{
    private String name;
    Star(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
//粉丝
class Fans
{
    private String name;
    Fans(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
//媒体公司
class Company
{
    private String name;
    Company(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
로그인 후 복사
로그인 후 복사
🎜 🎜이때 면적을 계산하는 것도 필요합니다.🎜🎜
粉丝韩丞与明星林心如见面了。
中国传媒有限公司与明星林心如洽淡业务。
로그인 후 복사
로그인 후 복사
🎜🎜이 변경이 완료되면 전혀 문제 없습니다. 그러나 실제 생산 환경에서는 상황이 더 복잡하고 변경 사항이 많은 부분에 관련되어 전체 신체에 영향을 미칠 수 있습니다. 또한 이전에 작성되고 테스트된 일부 기능을 다시 테스트해야 하며, 이로 인해 일부 기능을 사용할 수 없게 됩니다. 🎜🎜향상된 버전, 면적 계산의 공용 로직을 인터페이스로 전환: 🎜🎜rrreee🎜🎜이러한 방식으로 요구사항이 변경되어 원형 면적을 계산해야 할 때, 순환 클래스를 생성하고 Shape 인터페이스를 구현하기만 하면 됩니다:<br/>🎜🎜rrreee🎜

计算三角形面积、四边形面积... 的时候,我们只需让它们去实现 Shape 接口即可,无需修改源代码。

리히터 대체 원리


리히터 대체 원리는 상속에 관한 몇 가지 원칙, 즉 상속을 사용해야 하는 경우와 상속을 사용하지 말아야 하는 경우, 그리고 그 기반이 되는 원칙을 주로 설명합니다. Liskov 대체는 원래 상속 재사용의 기초입니다. 이는 기본 클래스와 하위 클래스 간의 관계를 반영하고 열기 및 닫기 원칙을 보완하며 추상화를 달성하기 위한 특정 단계를 규정합니다.

리히터 치환 원리의 역할

리히터 치환 원리의 주요 기능은 다음과 같습니다.

  • 리히터 치환 원리는 개폐 원리를 구현하는 중요한 방법 중 하나입니다.

  • 상속에서 상위 클래스를 재정의하여 재사용성이 떨어지는 단점을 극복합니다.

  • 행동의 정확성을 보장합니다. 즉, 클래스 확장으로 인해 기존 시스템에 새로운 오류가 발생하지 않으므로 코드 오류 가능성이 줄어듭니다.

  • 프로그램의 견고성을 강화하는 동시에 변경 시 매우 우수한 호환성을 달성하고, 프로그램의 유지 관리성과 확장성을 개선하며, 요구 사항 변경 시 발생하는 위험을 줄입니다.

리히터 대체 원리 구현 방법(상속)

리히터 대체 원리는 일반적으로 말합니다. 하위 클래스는 상위 클래스의 기능을 확장할 수 있지만 상위 클래스의 원래 기능을 변경할 수는 없습니다. 즉, 하위 클래스가 상위 클래스를 상속할 때 새 기능을 완성하기 위해 새 메서드를 추가하는 것 외에는 상위 클래스의 메서드를 재정의하지 마십시오.

위의 이해를 바탕으로 Liskov 대체 원칙의 정의는 다음과 같이 요약할 수 있습니다.

  • 하위 클래스는 상위 클래스의 추상 메서드를 구현할 수 있지만 상위 클래스의 비추상 메서드를 재정의할 수는 없습니다. class

  • 서브클래스에 고유한 메소드를 추가할 수 있습니다

  • 하위 클래스의 메소드가 상위 클래스의 메소드를 대체할 때 메소드의 전제조건(예: 메소드의 입력 매개변수) )는 상위 클래스의 메소드보다 느슨합니다

  • 하위 클래스의 메소드가 상위 클래스의 메소드를 구현하는 경우(추상 메소드 덮어쓰기/오버로딩 또는 구현), 메소드의 사후 조건(예: 출력/ 메서드의 반환 값)은 부모 클래스의 반환 값과 더 엄격하거나 동일합니다

부모 클래스 메서드를 다시 작성하여 새 함수를 작성하는 것은 간단하지만 전체 상속 시스템의 재사용성은 상대적으로 낮습니다. 다형성이 자주 사용되면 프로그램 실행 오류가 발생할 확률이 매우 높습니다.

프로그램이 Liskov 대체 원칙을 위반하면 상속된 클래스의 객체에 기본 클래스가 나타나는 런타임 오류가 발생합니다.

이때 수정 방법은 원래 상속 관계를 취소하고 둘 사이의 관계를 다시 설계하는 것입니다.

리스코프 치환 원리의 예와 관련하여 가장 유명한 것은 "정사각형은 직사각형이 아니다"입니다. 물론, 삶에도 비슷한 예가 많이 있습니다. 예를 들어, 펭귄, 타조, 키위는 생물학적 관점에서는 새로 분류되지만 계급 상속 관계에서는 비행 기능을 하는 "새" 능력을 물려받을 수 없기 때문에 "새"의 하위 클래스로 정의할 수 없습니다. 마찬가지로 "풍선 물고기"는 헤엄칠 수 없기 때문에 "물고기"의 하위 범주로 정의할 수 없으며, "장난감 대포"는 적을 폭파할 수 없으므로 "대포" 등의 하위 범주로 정의할 수 없습니다.

정사각형과 직사각형에 대한 가장 좋은 방법은 다른 상위 클래스를 추가하는 동시에 이 상위 클래스에서 상속하는 것입니다.

종속성 반전(와이어 드로잉 세부 정보)


종속성 반전 원칙은 개방-폐쇄 원칙을 구현하는 중요한 방법 중 하나이며, 이는 클라이언트와 구현 모듈 간의 결합을 줄입니다.

소프트웨어 디자인에서는 세부 사항이 변경 가능하지만 추상화 계층은 상대적으로 안정적이므로 추상화를 기반으로 구축된 아키텍처는 세부 사항을 기반으로 구축된 아키텍처보다 훨씬 안정적입니다. 여기서 추상은 인터페이스 또는 추상 클래스를 나타내고 세부 정보는 특정 구현 클래스를 나타냅니다.

인터페이스 또는 추상 클래스를 사용하는 목적은 특정 작업을 포함하지 않고 사양 및 계약을 공식화하고 세부 정보를 표시하는 작업을 구현 클래스에 맡기는 것입니다.

의존역전 원리의 기능

의존역전 원리의 주요 기능은 다음과 같습니다.

  • 종속성 역전의 원리는 클래스 간의 결합을 줄일 수 있습니다.

  • 종속성 반전 원칙은 시스템의 안정성을 향상시킬 수 있습니다.

  • 종속성 역전 원칙은 병렬 개발로 인한 위험을 줄일 수 있습니다.

  • 종속성 반전 원칙은 코드의 가독성과 유지 관리성을 향상시킬 수 있습니다.

依赖倒置原则的实现方法

依赖倒置原则的目的是通过要面向接口的编程来降低类间的耦合性,所以我们在实际编程中只要遵循以下4点,就能在项目中满足这个规则。

  • 每个类尽量提供接口或抽象类,或者两者都具备。

  • 变量的声明类型尽量是接口或者是抽象类。

  • 任何类都不应该从具体类派生。

  • 使用继承时尽量遵循里氏替换原则。

依赖倒置原则在“顾客购物程序”中的应用。

分析:本程序反映了 “顾客类”与“商店类”的关系。商店类中有 sell() 方法,顾客类通过该方法购物以下代码定义了顾客类通过韶关网店 ShaoguanShop 购物

class Customer {
    public void shopping(ShaoguanShop shop) {
        //购物
        System.out.println(shop.sell());
    }
}
로그인 후 복사
로그인 후 복사

但是,这种设计存在缺点,如果该顾客想从另外一家商店(如婺源网店 WuyuanShop)购物,就要将该顾客的代码修改如下:

class Customer {
    public void shopping(WuyuanShop shop) {
        //购物
        System.out.println(shop.sell());
    }
}
로그인 후 복사
로그인 후 복사

顾客每更换一家商店,都要修改一次代码,这明显违背了开闭原则。

存在以上缺点的原因是:顾客类设计时同具体的商店类绑定了,这违背了依赖倒置原则。

解决方法是:定义“婺源网店”和“韶关网店”的共同接口 Shop,顾客类面向该接口编程,其代码修改如下:

class Customer {
    public void shopping(Shop shop) {
        //购物
        System.out.println(shop.sell());
    }
}

class Customer {
    public void shopping(Shop shop) {
        //购物
        System.out.println(shop.sell());
    }
}
로그인 후 복사
로그인 후 복사

这样,不管顾客类 Customer 访问什么商店,或者增加新的商店,都不需要修改原有代码了,其类如下图所示:


디자인 패턴의 6가지 원칙은 무엇입니까?
程序代码如下:

package principle;
public class DIPtest
{
    public static void main(String[] args)
    {
        Customer wang=new Customer();
        System.out.println("顾客购买以下商品:"); 
        wang.shopping(new ShaoguanShop()); 
        wang.shopping(new WuyuanShop());
    }
}
//商店
interface Shop
{
    public String sell(); //卖
}
//韶关网店
class ShaoguanShop implements Shop
{
    public String sell()
    {
        return "韶关土特产:香菇、木耳……"; 
    } 
}
//婺源网店
class WuyuanShop implements Shop
{
    public String sell()
    {
        return "婺源土特产:绿茶、酒糟鱼……"; 
    }
} 
//顾客
class Customer
{
    public void shopping(Shop shop)
    {
        //购物
        System.out.println(shop.sell()); 
    }
}
로그인 후 복사
로그인 후 복사

程序的运行结果如下:

顾客购买以下商品:
韶关土特产:香菇、木耳……
婺源土特产:绿茶、酒糟鱼……
로그인 후 복사
로그인 후 복사

인터페이스 분리 원칙(인터페이스)


인터페이스 분리 원칙(ISP)은 프로그래머가 부풀어 오른 인터페이스를 더 작고 더 구체적인 인터페이스로 분할하여 인터페이스에 고객이 관심 있는 방법만 포함되도록 최선을 다하도록 요구합니다.

2002년 Robert C. Martin은 "인터페이스 격리 원칙"을 다음과 같이 정의했습니다. 클라이언트는 자신이 사용하지 않는 방법에 의존하도록 강요받아서는 안 됩니다(클라이언트는 자신이 사용하지 않는 방법에 의존하도록 강요받아서는 안 됩니다). 이 원칙에는 또 다른 정의가 있습니다. 한 클래스의 다른 클래스에 대한 종속성은 가능한 가장 작은 인터페이스에 따라 달라져야 합니다.

위 두 가지 정의의 의미는 호출에 의존하는 모든 클래스에 대해 거대한 인터페이스를 구축하려고 하기보다는 각 클래스에 필요한 전용 인터페이스를 구축한다는 것입니다.

인터페이스 격리 원칙과 단일 책임 원칙은 둘 다 클래스의 응집력을 향상시키고 클래스 간의 결합을 줄여 캡슐화 개념을 구현하지만 서로 다릅니다.

  • 단일 책임 원칙은 다음에 중점을 둡니다. 책임. 인터페이스 격리 원칙은 인터페이스 종속성의 격리에 중점을 둡니다.

  • 단일 책임 원칙은 주로 프로그램의 구현 및 세부 사항을 목표로 하는 클래스를 제한합니다. 인터페이스 격리 원칙은 주로 추상화 구성 및 프로그램의 전체 프레임워크를 위해 인터페이스를 제한합니다.

인터페이스 격리 원칙의 장점

인터페이스 격리 원칙은 인터페이스를 제한하고 인터페이스에 대한 클래스의 종속성을 줄이는 것입니다. 인터페이스 격리 원칙을 따르면 다음과 같은 5가지 장점이 있습니다.

  • 비대해진 인터페이스를 여러 개의 작은 세분화된 인터페이스로 분해하면 외부 변경 사항의 확산을 방지하고 시스템의 유연성과 유지 관리 가능성을 향상시킬 수 있습니다.

  • 인터페이스 격리는 시스템의 응집력을 향상시키고 외부 상호 작용을 줄이며 시스템의 결합을 줄입니다.

  • 인터페이스의 세분성을 합리적으로 정의하면 시스템의 안정성을 보장할 수 있지만 정의가 너무 작으면 인터페이스가 너무 많아지고 정의가 복잡해집니다. 너무 크면 유연성이 저하됩니다. 맞춤형 서비스를 제공할 수 없으면 전체 프로젝트에 예상치 못한 위험이 발생합니다.

  • 전체 인터페이스의 정의는 인터페이스 상속을 통해 달성할 수 있기 때문에 여러 특수 인터페이스를 사용하면 객체의 계층 구조를 반영할 수도 있습니다.

  • 프로젝트 엔지니어링에서 코드 중복을 줄일 수 있습니다. 지나치게 큰 인터페이스에는 일반적으로 사용되지 않는 메서드가 많이 포함되어 있습니다. 이 인터페이스를 구현할 때 중복된 코드를 디자인해야 합니다.

인터페이스 격리 원칙을 구현하는 방법

인터페이스 격리 원칙을 구체적으로 적용할 때는 다음 규칙에 따라 측정해야 합니다.

  • 인터페이스는 최대한 작아야 하지만 제한이 있어야 합니다. 인터페이스는 하나의 하위 모듈 또는 비즈니스 로직만 제공합니다.

  • 인터페이스에 의존하는 클래스에 대한 서비스를 맞춤화하세요. 호출자에게 필요한 메서드만 제공하고 필요하지 않은 메서드는 차단합니다.

  • 환경을 이해하고 맹목적으로 따르기를 거부하세요. 각 프로젝트 또는 제품은 환경적 요인을 선택했습니다. 환경에 따라 인터페이스 분할에 대한 표준이 다릅니다.

  • 결속력을 향상하고 외부 상호 작용을 줄입니다. 인터페이스에서 가장 적은 메서드를 사용하여 가장 많은 작업을 수행하도록 합니다.

인터페이스 격리의 경우 단일 책임에서 언급된 예를 참조할 수 있습니다.

public interface UserService {
    
    public void login(String username, String password);
    public void register(String email, String username, String password);
    public void logError(String msg);
    public void sendEmail(String email);
    
}
로그인 후 복사
로그인 후 복사
로그인 후 복사

这时候,应该就能理解拆分的好处了。

迪米特法则 (类与类之间的关系)


迪米特法则(Law of Demeter,LoD)又叫作最少知识原则(Least Knowledge Principle,LKP),产生于 1987 年美国东北大学(Northeastern University)的一个名为迪米特(Demeter)的研究项目,由伊恩·荷兰(Ian Holland)提出,被 UML 创始者之一的布奇(Booch)普及,后来又因为在经典著作《程序员修炼之道》(The Pragmatic Programmer)提及而广为人知。

迪米特法则的定义是:只与你的直接朋友交谈,不跟“陌生人”说话(Talk only to your immediate friends and not to strangers)。其含义是:如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。

迪米特法则中的“朋友”是指:当前对象本身、当前对象的成员对象、当前对象所创建的对象、当前对象的方法参数等,这些对象同当前对象存在关联、聚合或组合关系,可以直接访问这些对象的方法。

迪米特法则的优点

迪米特法则要求限制软件实体之间通信的宽度和深度,正确使用迪米特法则将有以下两个优点。

  • 降低了类之间的耦合度,提高了模块的相对独立性。

  • 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。

但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。

迪米特法则的实现方法

从迪米特法则的定义和特点可知,它强调以下两点:

  • 从依赖者的角度来说,只依赖应该依赖的对象。

  • 从被依赖者的角度说,只暴露应该暴露的方法。

所以,在运用迪米特法则时要注意以下 6 点。

  • 在类的划分上,应该创建弱耦合的类。类与类之间的耦合越弱,就越有利于实现可复用的目标。

  • 在类的结构设计上,尽量降低类成员的访问权限。

  • 在类的设计上,优先考虑将一个类设置成不变类。

  • 在对其他类的引用上,将引用其他对象的次数降到最低。

  • 不暴露类的属性成员,而应该提供相应的访问器(set 和 get 方法)。

  • 谨慎使用序列化(Serializable)功能

明星与经纪人的关系实例。

分析:明星由于全身心投入艺术,所以许多日常事务由经纪人负责处理,如与粉丝的见面会,与媒体公司的业务洽淡等。这里的经纪人是明星的朋友,而粉丝和媒体公司是陌生人,所以适合使用迪米特法则,其类图如下图所示。


디자인 패턴의 6가지 원칙은 무엇입니까?
代码如下:

package principle;
public class LoDtest
{
    public static void main(String[] args)
    {
        Agent agent=new Agent();
        agent.setStar(new Star("林心如"));
        agent.setFans(new Fans("粉丝韩丞"));
        agent.setCompany(new Company("中国传媒有限公司"));
        agent.meeting();
        agent.business();
    }
}
//经纪人
class Agent
{
    private Star myStar;
    private Fans myFans;
    private Company myCompany;
    public void setStar(Star myStar)
    {
        this.myStar=myStar;
    }
    public void setFans(Fans myFans)
    {
        this.myFans=myFans;
    }
    public void setCompany(Company myCompany)
    {
        this.myCompany=myCompany;
    }
    public void meeting()
    {
        System.out.println(myFans.getName()+"与明星"+myStar.getName()+"见面了。");
    }
    public void business()
    {
        System.out.println(myCompany.getName()+"与明星"+myStar.getName()+"洽淡业务。");
    }
}
//明星
class Star
{
    private String name;
    Star(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
//粉丝
class Fans
{
    private String name;
    Fans(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
//媒体公司
class Company
{
    private String name;
    Company(String name)
    {
        this.name=name;
    }
    public String getName()
    {
        return name;
    }
}
로그인 후 복사
로그인 후 복사

程序的运行结果如下:

粉丝韩丞与明星林心如见面了。
中国传媒有限公司与明星林心如洽淡业务。
로그인 후 복사
로그인 후 복사

 到此,设计模式的六大原则就讲完了。

更多编程相关知识,请访问:编程教学!!

위 내용은 디자인 패턴의 6가지 원칙은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿