SOLID JavaScript에 `S` 입력

7541 단어
S - SRP(Single-Responsibility Principle)는 SOLID 주체가 적용되는 첫 번째 설계 주체입니다.

SOLID is an Object oriented design principal. It stands for:

  • S - Single-responsiblity Principle
  • O - Open-closed Principle
  • L - Liskov Substitution Principle
  • I - Interface Segregation Principle
  • D - Dependency Inversion Principle

The principals were originally created and promoted by Robert Martin (aka Uncle Bob).



SRP는 단순히 클래스와 함수가 단일 책임만 가져야 한다고 명시합니다. 이것은 원칙적으로는 매우 쉽게 들리지만 실제로는 특히 React에서는 약간 더 미묘한 차이가 있을 수 있습니다. 아티스트와 아티스트의 세부 사항을 처리하는 클래스로 설명하겠습니다.

// Wrong!
class Artist {
    constructor(id) {
        this.id = id;
        this.getBio()
        this.getSingles();
        this.getAlbums()
    }

    getBio() {
        fetch(`/artist/${this.id}/bio/`)
       //... do stuff with data
    }

    getSingles() {
        fetch(`/artist/${this.id}/singles/`)
        //... do stuff with data
    }

    getAlbums() {
        fetch(`/artist/${this.id}/articles/`)
        //... do stuff with data
    }
}

const theArtist = new Artist('formerlyKnownAsPrince');



이 예에서 Artist 클래스는 여러 가지를 담당합니다. 아티스트를 대표해야 하지만 약력, 싱글 및 앨범을 가져오는 책임도 있습니다.

이것이 왜 나쁜가요? 예를 들어 getBio와 같은 추가 속성을 갖기 위해 this.fetchedData = true를 업데이트해야 하는 경우 클래스의 다른 부분이 손상될 수 있습니다. 정말 조심해서 this.fetchedBio와 같은 더 구체적인 변수를 사용할 수 있지만 애플리케이션과 클래스/함수가 복잡할수록 이를 관리하고 추적하기가 더 어려워집니다. 대신 책임을 분할하고 하나의 책임만 처리하는 다른 클래스와 함수를 만들 수 있습니다.

// Right!

function getBio(id) {
    fetch(`/artist/${id}/bio/`)
    //... do stuff with data
    return bio;
}

function getSingles(id) {
    fetch(`/artist/${id}/singles/`)
    //... do stuff with data
    return singles;
}

function getAlbums(id) {
    fetch(`/artist/${this.id}/articles/`)
    //... do stuff with data
    return albums;
}

class Artist {
    constructor(bio, singles, albums) {
        this.bio = bio;
        this.singles = singles;
        this.albums = albums;
    }
}

function getArtistDetails(id) {
    const bio = getBio(id);
    const singles = getSingles(id);
    const albums = getAlbums(id);

    return { bio, singles, albums };
}

const { bio, singles, albums } = getArtistDetails('formerlyKnownAsPrince')

const theArtist = new Artist(bio, singles, albums);



이제 Artist 클래스를 분할하고 메서드를 순수 함수로 옮겼습니다. 각 사람에게는 단일한 책임이 있습니다.

하지만 3가지(바이오, 싱글, 앨범)를 가져오는 역할을 하는 getArtistDetails 함수는 어떻습니까? 데이터를 가져오는 것이 정말 하나의 책임이기 때문에 괜찮습니다!

이제 해당 아티스트를 저장하려면 하나의 책임만 가질 수 있는 새 클래스Saver를 만들 수 있습니다! 우리는 Artist에 어떤 다른 메서드가 있는지 또는 어떤 속성을 가질 수 있는지에 대해 걱정할 필요가 없습니다. Artist를 가져와서 다른 속성이나 메서드를 손상시킬 염려 없이 저장할 수 있습니다.

//... the rest
const theArtist = new Artist(bio, singles, albums); // from before

class Saver {
    save (data) {
        // something to save the data
    }
}

const saver = new Saver();

saver.save(theArtist);



반응하다


component classes 또는 functional components 를 생성하든 동일한 원칙이 반응에 적용됩니다.

// Wrong
const Artist = ({ id }) => {
    const [bio, setBio] = useState(null);
    const [singles, setSingles] = useState([]);
    const [albums, setAlbums] = useState([]);

    useEffect(()=> {
        fetch(`/artist/${id}/bio/`)
            .then(data => {
                setBio(data);
            })
    }, [id])

    useEffect(()=> {
        fetch(`/artist/${id}/singles/`)
            .then(data => {
                setSingles(data);
            })
    }, [id])

    useEffect(()=> {
        fetch(`/artist/${id}/albums/`)
            .then(data => {
                setAlbums(data);
            })
    }, [id])

    const singlesList = singles.map(single => (
        <li key={single.id}>{single.title} - {single.releaseDate}</li>
    ));

    const albumsList = albums.map(album => (
        <li key={album.id}>{album.title} <img src={album.artWorkUrl} /></li>
    ));

    return (
        <div>
            { bio ? <h2>{bio.name}</h2> : null }
            <ul>{singlesList}</ul>
            <ul>{albumsList}</ul>
        </div>
    )
}



구성 요소에 문제가 있는 몇 가지 일이 있습니다. 데이터 검색/상태 논리가 있고 모두 동일한 구성 요소에 프레젠테이션 논리가 있습니다. 주요 구성 요소와 함께 각 목록의 렌더링이 있습니다. 너무 많은 일을 하기 때문에 컴포넌트를 재사용하거나 다른 용도로 변경하기가 매우 어렵습니다.

고치자! 프레젠테이션 UI 및 논리에서 데이터 논리를 분리합니다. UI의 각 부분에는 전용 단일 책임 구성 요소가 있습니다.


const Bio = ({bio}) => {
    if (!bio) return null;

    return <h2>{ bio.name}</h2>;
}

const SingleList = ({singles}) => {
    const singlesList = singles.map(single => (
        <li key={single.id}>{single.title} - {single.releaseDate}</li>
    ));

    return (
        <ul>
            {singlesList}
        </ul>
    )
}

const AlbumList = ({albums}) => {
    const albumsList = albums.map(album => (
        <li key={album.id}>{album.title} <img src={album.artWorkUrl} /></li>
    ));

    return (
        <ul>
            {albumsList}
        </ul>
    )
}

const ArtistCard = ({bio, singles, albums}) => (
    <div>
        <Bio bio={bio} />
        <SingleList singles={singles} />
        <AlbumList albums={albums} />
    </div>
)

const Artist = ({ id }) => {
    const [bio, setBio] = useState(null);
    const [singles, setSingles] = useState([]);
    const [albums, setAlbums] = useState([]);

    useEffect(()=> {
        fetch(`/artist/${id}/bio/`)
            .then(data => {
                setBio(data);
            })
    }, [id])

    useEffect(()=> {
        fetch(`/artist/${id}/singles/`)
            .then(data => {
                setSingles(data);
            })
    }, [id])

    useEffect(()=> {
        fetch(`/artist/${id}/albums/`)
            .then(data => {
                setAlbums(data);
            })
    }, [id])

    return <ArtistCard bio={bio} singles={singles} albums={albums} />
}



이제 다른 위치에서 UI를 재사용할 수 있음을 의미합니다. 예를 들어 아티스트 목록에 ArtistCard를 사용할 수 있습니다. 동일한 UI를 다시 만들거나 논리를 처리할 필요가 없었습니다. 그냥 작동합니다 ™ .

const ArtistList = ({listOfArtists}) => {
    const artists = listOfArtists.map(({bio, singles, albums}) => (
        <ArtistCard bio={bio} singles={singles} albums={albums} />
    ));

    return <div>{artists}</div>;
}



우리가 만든 구성 요소는 모두 순수하기 때문에(동일한 입력이 주어지면 동일한 값을 반환함) 이것은 우리가 2가지 매우 유용한 이점을 얻는다는 것을 의미합니다.
  • 동일한 입력이 동일한 출력을 반환하므로 reactmemo를 사용하여 쉽게 최적화할 수 있습니다.
  • 같은 입력이 주어졌을 때 출력이 항상 동일하고 데이터 소스에 의존할 필요가 없기 때문에 테스트가 쉽습니다.

  • 요약



    단일 책임 원칙은 클래스, 기능 및 구성 요소를 보다 안정적이고 재사용 가능하며 최적화 가능하고 테스트 가능하게 만들기 때문에 배우고 사용할 수 있는 훌륭한 기본 원칙입니다.

    다음 사항을 기억하십시오.
  • 생성하는 모든 기능을 확인하여 일회용 기능으로 이동할 수 있는 더 작은 부품이 있는지 확인하십시오. 2. 함수가 2개 이상의 다른 작업을 수행하지 않는지 확인하십시오.
  • 반복
  • 좋은 웹페이지 즐겨찾기